A worked price
500 characters cost $0.12. The published rate is $0.00024 per character; every quote rounds up to the service's $0.001 grid. The unpaid 402 remains authoritative.
const text = ('The number in the 402 is the number that settles. ').repeat(11).slice(0, 500);Default voice: 21m00Tcm4TlvDq8ikWAM. Supply voice only when you already know an ElevenLabs voice id. Maximum 5000 characters.
A complete, capped buyer
This Node recipe reads the free quote, rejects anything above $0.12, lets x402-fetch build the payment from the returned envelope, retries the identical request, checks the response, and saves kino-tts.mp3 without overwriting an existing file.
// npm install x402-fetch@1.2.0 viem@2.56.0
import { writeFile } from 'node:fs/promises';
import { privateKeyToAccount } from 'viem/accounts';
import { wrapFetchWithPayment } from 'x402-fetch';
const URL = 'https://kino402.com/v1/audio/tts';
const TEXT = ('The number in the 402 is the number that settles. ').repeat(11).slice(0, 500);
const BODY = JSON.stringify({ text: TEXT });
const MAX_SPEND_ATOMIC = 120000n; // $0.12 USDC maximum
const request = { method: 'POST', headers: { 'content-type': 'application/json' }, body: BODY };
// Read the quote for this exact body before signing anything.
const quoteResponse = await fetch(URL, request);
if (quoteResponse.status !== 402) throw new Error(`expected quote, got ${quoteResponse.status}`);
const quote = await quoteResponse.json();
const amount = BigInt(quote.accepts?.[0]?.maxAmountRequired ?? '-1');
if (amount < 0n || amount > MAX_SPEND_ATOMIC) throw new Error(`quote exceeds max spend: ${amount}`);
const account = privateKeyToAccount(process.env.BUYER_PRIVATE_KEY);
const selectBase = requirements => requirements.find(r =>
r.network === 'base' || r.network === 'eip155:8453');
const paidFetch = wrapFetchWithPayment(fetch, account, MAX_SPEND_ATOMIC, selectBase);
const audioResponse = await paidFetch(URL, request); // handles the 402 retry and payment header
if (!audioResponse.ok) throw new Error(`TTS failed: ${audioResponse.status} ${await audioResponse.text()}`);
if (!(audioResponse.headers.get('content-type') || '').startsWith('audio/mpeg')) throw new Error('expected audio/mpeg');
await writeFile('kino-tts.mp3', Buffer.from(await audioResponse.arrayBuffer()), { flag: 'wx' });Save the code as buyer.mjs, run npm install x402-fetch@1.2.0 viem@2.56.0, then run BUYER_PRIVATE_KEY=0x... node buyer.mjs. The key must control a Base wallet funded with Base USDC; this recipe selects the Base rail.