Documentation Index
Fetch the complete documentation index at: https://v1-docs.zcombinator.io/llms.txt
Use this file to discover all available pages before exploring further.
API Architecture
The Token Launch API uses a partial transaction signing model that provides security while maintaining user control:
Phase 1: Prepare
- API creates unsigned transaction
- User receives transaction for signing
- No sensitive data exposed
Phase 2: Complete
- User signs transaction with their wallet
- API adds protocol signature
- Transaction submitted to blockchain
- User Control: Users sign all transactions
- Key Isolation: API never sees user private keys
- Transparency: All operations visible on-chain
- Verification: Users can inspect transactions before signing
- Stateless: No session management required
- RESTful: Standard HTTP methods and status codes
- JSON: All data exchange in JSON format
- Rate Limited: IP-based throttling for fair usage
Integration Patterns
Web Application Integration
Perfect for dApps and web interfaces:
import { useWallet } from '@solana/wallet-adapter-react';
import { Transaction } from '@solana/web3.js';
import bs58 from 'bs58';
function TokenLauncher() {
const { publicKey, signTransaction } = useWallet();
const [launching, setLaunching] = useState(false);
const launchToken = async (tokenData) => {
if (!publicKey || !signTransaction) return;
setLaunching(true);
try {
// Step 1: Create unsigned transaction
const launchResponse = await fetch('https://api.zcombinator.io/launch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...tokenData,
payerPublicKey: publicKey.toString(),
quoteToken: "SOL" // Optional: "SOL" or "ZC"
})
});
const launchResult = await launchResponse.json();
// Step 2: Sign transaction
const transaction = Transaction.from(bs58.decode(launchResult.transaction));
const signedTransaction = await signTransaction(transaction);
// Step 3: Confirm launch
const confirmResponse = await fetch('https://api.zcombinator.io/confirm-launch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
signedTransaction: bs58.encode(signedTransaction.serialize()),
baseMint: launchResult.baseMint,
metadataUrl: launchResult.metadataUrl,
name: tokenData.name,
symbol: tokenData.symbol,
payerPublicKey: publicKey.toString()
})
});
const confirmResult = await confirmResponse.json();
if (confirmResult.success) {
console.log('Token launched!', confirmResult.transactionSignature);
return confirmResult;
}
} catch (error) {
console.error('Launch failed:', error);
throw error;
} finally {
setLaunching(false);
}
};
return (
<div>
{/* Your UI components */}
<button onClick={() => launchToken(formData)} disabled={launching}>
{launching ? 'Launching...' : 'Launch Token'}
</button>
</div>
);
}
Backend Service Integration
For automated systems and batch operations:
from flask import Flask, request, jsonify
import requests
from solders.pubkey import Pubkey
from solders.transaction import Transaction
import base58
app = Flask(__name__)
class TokenLaunchService:
def __init__(self):
self.api_base = 'https://api.zcombinator.io'
self.session = requests.Session()
def launch_token(self, token_data, wallet_keypair):
"""Launch a token programmatically"""
# Step 1: Create launch transaction
launch_response = self.session.post(
f'{self.api_base}/launch',
json={
**token_data,
'payerPublicKey': str(wallet_keypair.pubkey()),
'quoteToken': 'SOL' # Optional: 'SOL' or 'ZC'
}
)
launch_response.raise_for_status()
launch_result = launch_response.json()
# Step 2: Sign transaction
transaction_bytes = base58.b58decode(launch_result['transaction'])
transaction = Transaction.from_bytes(transaction_bytes)
# Sign with your keypair
transaction.sign([wallet_keypair])
# Step 3: Confirm launch
confirm_response = self.session.post(
f'{self.api_base}/confirm-launch',
json={
'signedTransaction': base58.b58encode(bytes(transaction)).decode(),
'baseMint': launch_result['baseMint'],
'metadataUrl': launch_result['metadataUrl'],
'name': token_data['name'],
'symbol': token_data['symbol'],
'payerPublicKey': str(wallet_keypair.pubkey())
}
)
confirm_response.raise_for_status()
return confirm_response.json()
@app.route('/internal/launch-token', methods=['POST'])
def internal_launch():
"""Internal endpoint for launching tokens"""
data = request.get_json()
# Your wallet management logic here
wallet_keypair = load_wallet_keypair(data['wallet_id'])
service = TokenLaunchService()
result = service.launch_token(data['token_data'], wallet_keypair)
return jsonify(result)
Mobile App Integration
For React Native and mobile applications:
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { Transaction } from '@solana/web3.js';
import bs58 from 'bs58';
export const useTokenLauncher = () => {
const { connection } = useConnection();
const { publicKey, signTransaction } = useWallet();
const launchToken = async (tokenData) => {
if (!publicKey || !signTransaction) {
throw new Error('Wallet not connected');
}
try {
// Create unsigned transaction
const response = await fetch('https://api.zcombinator.io/launch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...tokenData,
payerPublicKey: publicKey.toString(),
quoteToken: "SOL", // Optional: "SOL" or "ZC"
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
const result = await response.json();
// Sign transaction
const transaction = Transaction.from(bs58.decode(result.transaction));
const signedTransaction = await signTransaction(transaction);
// Confirm launch
const confirmResponse = await fetch('https://api.zcombinator.io/confirm-launch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
signedTransaction: bs58.encode(signedTransaction.serialize()),
baseMint: result.baseMint,
metadataUrl: result.metadataUrl,
name: tokenData.name,
symbol: tokenData.symbol,
payerPublicKey: publicKey.toString(),
}),
});
const confirmResult = await confirmResponse.json();
if (!confirmResponse.ok) {
throw new Error(confirmResult.error);
}
return confirmResult;
} catch (error) {
console.error('Token launch error:', error);
throw error;
}
};
return { launchToken };
};
Common Integration Patterns
Error Handling
async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
// Rate limited - wait before retry
const delay = Math.min(1000 * (2 ** attempt), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
if (response.status >= 500) {
// Server error - retry with backoff
if (attempt < maxRetries - 1) {
const delay = 1000 * (2 ** attempt);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (2 ** attempt)));
}
}
}
const handleLaunch = async (tokenData) => {
setStatus('Creating transaction...');
try {
const launchResult = await createLaunchTransaction(tokenData);
setStatus('Please sign the transaction in your wallet...');
const signedTransaction = await signTransaction(launchResult.transaction);
setStatus('Submitting to blockchain...');
const confirmResult = await confirmLaunch(signedTransaction);
setStatus('Token launched successfully!');
return confirmResult;
} catch (error) {
if (error.message.includes('rate limit')) {
setStatus('Rate limited. Please wait before trying again.');
} else if (error.message.includes('User rejected')) {
setStatus('Transaction cancelled by user.');
} else {
setStatus('Launch failed. Please try again.');
}
throw error;
}
};
const validateTokenData = (data) => {
const errors = [];
if (!data.name?.trim()) errors.push('Token name is required');
if (!data.symbol?.trim()) errors.push('Token symbol is required');
if (data.caEnding && data.caEnding.length > 3) {
errors.push('CA ending must be 3 characters or less');
}
if (data.caEnding && /[0OIl]/.test(data.caEnding)) {
errors.push('CA ending contains invalid characters');
}
try {
new PublicKey(data.payerPublicKey);
} catch {
errors.push('Invalid payer public key');
}
return errors;
};
const launchToken = async (tokenData) => {
const validationErrors = validateTokenData(tokenData);
if (validationErrors.length > 0) {
throw new Error(`Validation failed: ${validationErrors.join(', ')}`);
}
// Proceed with launch...
};
class BatchAPIClient {
constructor() {
this.requestQueue = [];
this.processing = false;
}
async queueRequest(endpoint, options) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ endpoint, options, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.requestQueue.length === 0) return;
this.processing = true;
while (this.requestQueue.length > 0) {
const batch = this.requestQueue.splice(0, 4); // Respect rate limit
const promises = batch.map(async ({ endpoint, options, resolve, reject }) => {
try {
const response = await fetch(endpoint, options);
const result = await response.json();
resolve(result);
} catch (error) {
reject(error);
}
});
await Promise.all(promises);
// Wait for rate limit window if needed
if (this.requestQueue.length > 0) {
await new Promise(resolve => setTimeout(resolve, 2 * 60 * 1000));
}
}
this.processing = false;
}
}
class CachedAPIClient {
constructor() {
this.cache = new Map();
this.cacheTimeout = 60000; // 1 minute
}
async get(url, cacheable = false) {
const cacheKey = url;
if (cacheable && this.cache.has(cacheKey)) {
const { data, timestamp } = this.cache.get(cacheKey);
if (Date.now() - timestamp < this.cacheTimeout) {
return data;
}
}
const response = await fetch(url);
const data = await response.json();
if (cacheable) {
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
}
return data;
}
// Cache token verification permanently
async verifyToken(address) {
return this.get(
`https://api.zcombinator.io/verify-token/${address}`,
true // Cache token verification
);
}
// Don't cache claim eligibility (changes over time)
async getClaimEligibility(tokenAddress, wallet) {
return this.get(
`https://api.zcombinator.io/claims/${tokenAddress}?wallet=${wallet}`,
false // Don't cache eligibility
);
}
}
Best Practices
- Never store private keys in client-side applications
- Validate all inputs before sending to API
- Use HTTPS for all API communications
- Implement proper error handling to avoid exposing sensitive information
- Verify transaction contents before signing
- Show loading states during API calls
- Provide clear error messages with actionable guidance
- Implement proper retry logic for transient failures
- Cache appropriate responses to reduce API calls
- Respect rate limits to maintain service availability
- Track API response times and error rates
- Monitor rate limit usage to avoid 429 errors
- Log transaction signatures for debugging
- Implement health checks using
/health endpoint
- Set up alerts for persistent API failures