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
- Create a key. Sign up, open API keys and create one. The full key is shown once, so copy it then.
- Top up. Add from $5 in Billing. Requests without balance answer 402.
- Change the base URL. Point your SDK at
https://refillhub.biz.id/api/v1. The marked line is the only change.
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.
curl https://refillhub.biz.id/api/v1/modelsStreaming
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.
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.
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" }}| Status | Code | What happened |
|---|---|---|
| 400 | invalid_jsoninvalid_request_error | The body is not valid JSON. |
| 400 | invalid_bodyinvalid_request_error | A field failed validation. The message names the field. |
| 401 | missing_api_keyauthentication_error | No Authorization: Bearer header was sent. |
| 401 | invalid_api_keyauthentication_error | The key is unknown or has been revoked. |
| 402 | insufficient_balanceinsufficient_quota | Your balance cannot cover the prompt. Top up and retry. |
| 402 | key_spend_limitinsufficient_quota | This key reached the spend limit you set. Raise it in the dashboard. |
| 403 | account_suspendedpermission_error | The account that owns this key is suspended. Contact support. |
| 404 | model_not_foundinvalid_request_error | The model id is not in the catalog. List ids at GET /api/v1/models. |
| 413 | body_too_largeinvalid_request_error | The request body is over 4 MB. |
| 429 | rate_limitedrate_limit_error | This key used its requests for the minute. Wait for Retry-After. |
| 429 | upstream_rate_limitedrate_limit_error | The model provider is rate limiting. Retry shortly. |
| 502 | upstream_authupstream_error | The provider rejected this server’s credentials. Nothing is charged. |
| 502 | upstream_failedupstream_error | The provider failed or returned an empty stream. |
| 503 | upstream_not_configuredupstream_error | No model provider is configured on this server. |
| 504 | upstream_timeoutupstream_error | The provider did not answer in time. |
| Provider’s | upstream_rejectedinvalid_request_error | The 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
Pick from 3 models, priced per million tokens.
Create a key and send the quickstart request.