Examples
Usage-based billing for OpenAI API apps
Updated Oct 25, 2026
These examples add usage-based billing to an app built on the OpenAI Responses API with the official TypeScript SDK. Use meter() to charge for the tokens each request actually uses, or charge() for a flat price per request. You set the prices; Payfer takes them from the person’s balance and pays you what’s left after its fee.
You’ll need a connected user’s paymentUserId from the quickstart, and both SDKs on your server.
npm install payfer openaiimport OpenAI from "openai";
import { Payfer } from "payfer/server";
const openai = new OpenAI(); // reads OPENAI_API_KEY
const payfer = new Payfer({ secretKey: process.env.PAYFER_SECRET_KEY! });Bill OpenAI usage per token
Count the input tokens first, then reserve the most the request could cost: that input plus a full max_output_tokens of output. Payfer checks the person’s balance and allowance before the model runs, collects the price of the tokens OpenAI reports, and releases the rest.
const MODEL = "gpt-5.5";
const MAX_OUTPUT_TOKENS = 4000;
// What you charge, in dollars per million tokens, which is also
// microdollars per token. Set these above your OpenAI cost.
const PRICE = { input: 6, output: 30 };
// Payfer amounts are dollar strings with up to 6 decimal places
// (whole microdollars), and at least $0.005.
const toAmount = (micros: number) =>
(Math.max(Math.ceil(micros), 5000) / 1e6).toFixed(6);
const priceOf = (input: number, output: number) =>
toAmount(input * PRICE.input + output * PRICE.output);
export async function ask(
paymentUserId: string,
question: string,
operationId: string,
) {
// The most this request can cost: the exact input, plus a full
// max_output_tokens of output.
const counted = await openai.responses.inputTokens.count({
model: MODEL,
input: question,
});
const maxAmount = priceOf(counted.input_tokens, MAX_OUTPUT_TOKENS);
const reservation = { paymentUserId, maxAmount, idempotencyKey: operationId };
return payfer.meter(reservation, async () => {
const response = await openai.responses.create({
model: MODEL,
input: question,
max_output_tokens: MAX_OUTPUT_TOKENS,
});
// Throwing releases the reservation, so nothing is charged.
if (!response.usage) throw new Error("OpenAI returned no usage");
const { input_tokens, output_tokens } = response.usage;
return {
amount: priceOf(input_tokens, output_tokens),
value: response.output_text,
};
});
}output_tokens includes reasoning tokens, and max_output_tokens caps both, so the price you collect never exceeds the reservation. If the person can’t cover the reservation, meter() throws funding_required or allowance_exceeded before OpenAI is called. If the request fails, the reservation is released and nothing is charged.
To stream instead, call openai.responses.stream() inside the callback, forward its events to your user, and price (await stream.finalResponse()).usage the same way.
Charge a flat price per OpenAI request
For a simple price per task, charge before calling OpenAI, so someone without funds never costs you a model call, and refund if the request fails. Reusing the operation’s idempotency key on a retry returns the same payment instead of charging twice.
// A flat price for each summary.
const PRICE_PER_SUMMARY = "0.02";
export async function summarize(
paymentUserId: string,
text: string,
operationId: string,
) {
// Charge first, so a user without funds never costs you an OpenAI call.
const payment = await payfer.charge({
paymentUserId,
amount: PRICE_PER_SUMMARY,
idempotencyKey: operationId,
});
try {
const response = await openai.responses.create({
model: "gpt-5.5",
instructions: "Summarize the text in three sentences.",
input: text,
});
return response.output_text;
} catch (err) {
// The summary failed, so give the money back.
await payfer.refund({
paymentId: payment.id,
idempotencyKey: operationId + ":refund",
});
throw err;
}
}