Skip to content

API docs

The Refillhub API speaks the OpenAI format: one base URL, one bearer key, the same request and response shapes. Each request is charged to your prepaid balance by the token.

Quickstart

  1. Create a key. Sign up, open API keys and create one. The full key is shown once, so copy it then.
  2. Top up. Add from $5 in Billing. Requests without balance answer 402.
  3. Change the base URL. Point your SDK at https://refillhub.biz.id/api/v1. The marked line is the only change.
client.ts
import OpenAI from "openai"; const client = new OpenAI({  baseURL: "https://refillhub.biz.id/api/v1",  apiKey: process.env.REFILLHUB_API_KEY,}); const reply = await client.chat.completions.create({  model: "gemini-3.1-flash-lite",  messages: [{ role: "user", content: "Explain token metering." }],}); console.log(reply.choices[0].message.content);

Authentication

Send the key on every request as Authorization: Bearer rh_…. Keys start with rh_. A missing header answers missing_api_key; a revoked or unknown key answers invalid_api_key. Keep keys on the server, never in browser code.

Endpoints

POST /api/v1/chat/completions

The OpenAI chat completions request. Model ids carry the provider prefix, for example gemini-3.1-flash-lite. Every response has an x-request-id header; non-streaming responses also carry x-refillhub-cost-usd, the amount charged.

GET /api/v1/models

Public, no key needed. Lists every enabled model with its context length and price per million input and output tokens, in the OpenAI list shape. The same data prints the models and prices page.

shell
curl https://refillhub.biz.id/api/v1/models

Streaming

Set stream: true for server-sent events. Deltas are forwarded as they arrive and usage is requested on the final chunk, so a streamed request is billed the same way as one that waits.

JavaScript
const stream = await client.chat.completions.create({  model: "gemini-3.1-flash-lite",  messages: [{ role: "user", content: "Write a haiku about refuelling." }],  stream: true,}); for await (const chunk of stream) {  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");}

Errors

A failure returns { "error": { "message", "type", "code" } } with a matching HTTP status. Branch on code; show message to people.

Response
HTTP/1.1 402 Payment Required {  "error": {    "message": "Your balance is too low for this request. Top up in the dashboard.",    "type": "insufficient_quota",    "code": "insufficient_balance"  }}

Error codes returned by the Refillhub API
StatusCodeWhat happened
400invalid_jsoninvalid_request_errorThe body is not valid JSON.
400invalid_bodyinvalid_request_errorA field failed validation. The message names the field.
401missing_api_keyauthentication_errorNo Authorization: Bearer header was sent.
401invalid_api_keyauthentication_errorThe key is unknown or has been revoked.
402insufficient_balanceinsufficient_quotaYour balance cannot cover the prompt. Top up and retry.
402key_spend_limitinsufficient_quotaThis key reached the spend limit you set. Raise it in the dashboard.
403account_suspendedpermission_errorThe account that owns this key is suspended. Contact support.
404model_not_foundinvalid_request_errorThe model id is not in the catalog. List ids at GET /api/v1/models.
413body_too_largeinvalid_request_errorThe request body is over 4 MB.
429rate_limitedrate_limit_errorThis key used its requests for the minute. Wait for Retry-After.
429upstream_rate_limitedrate_limit_errorThe model provider is rate limiting. Retry shortly.
502upstream_authupstream_errorThe provider rejected this server’s credentials. Nothing is charged.
502upstream_failedupstream_errorThe provider failed or returned an empty stream.
503upstream_not_configuredupstream_errorNo model provider is configured on this server.
504upstream_timeoutupstream_errorThe provider did not answer in time.
Provider’supstream_rejectedinvalid_request_errorThe provider refused the request. Its status and message are passed through.

When your balance covers the prompt but not the full output you asked for, max_tokens is lowered to what the balance can pay for instead of failing the call.

Limits and spend caps

Each key allows 600 requests a minute unless you set its own limit. A 429 rate_limited carries retry-after in seconds, plus x-ratelimit-limit-requests and x-ratelimit-remaining-requests. Request bodies are capped at 4 MB.

A key can carry a spend limit. Once it is spent the key answers key_spend_limit until you raise it. Usage in the dashboard lists every request with its model, tokens, cost and latency; prompt and completion text are not stored.

Where to next

Models

Pick from 3 models, priced per million tokens.

Get an API key

Create a key and send the quickstart request.