Account and catalog API
GET /v1/models, GET /v1/usage, and the public GET /api/v1/models/pricing: authentication, example responses, field types and units, and the response headers HopBase sets.
Three read-only endpoints tell your code which models a key can call, how much the key can still spend, and what every model costs at list price. None of them is billed, and none needs a positive balance.
| Endpoint | Authentication | What it returns |
|---|---|---|
GET /v1/models | Key: Authorization: Bearer sk-… or x-api-key: sk-… | The models this key's group serves |
GET /v1/usage | Key: Authorization: Bearer sk-… only | What this key can still spend, and its quota |
GET /api/v1/models/pricing | None | The public model catalog with list prices |
List models
curl https://api.hop-base.com/v1/models \
-H "Authorization: Bearer sk-your-key"The list contains only the models of the group the key is bound to, and it comes back complete in one response (no paging). Keys from OpenAI-compatible groups get an OpenAI-shaped list:
{
"object": "list",
"data": [
{
"id": "gpt-6-astra",
"object": "model",
"created": 1790222400,
"owned_by": "hopbase",
"capabilities": ["chat", "reasoning"],
"context_window": 1050000,
"context_length": 1050000,
"max_input_tokens": 1050000,
"max_output_tokens": 128000
}
]
}Keys from Claude groups get the Anthropic shape:
{
"object": "list",
"data": [
{
"id": "claude-opus-5-5",
"object": "model",
"type": "model",
"display_name": "Claude Opus 5.5",
"created_at": "2026-09-22T00:00:00Z"
}
],
"has_more": false,
"first_id": "claude-opus-5-5",
"last_id": "claude-haiku-4-5-20251001"
}| Field | Type | Meaning |
|---|---|---|
data[].id | string | The model ID to send in requests. This is the only field to rely on |
data[].capabilities | string[] | For example chat, reasoning, image_generation |
data[].image_only | boolean | true for image models; absent otherwise |
data[].context_window, context_length, max_input_tokens | integer, tokens | Same value in three spellings for client compatibility; absent when not published |
data[].max_output_tokens | integer, tokens | Absent when not published |
data[].created | integer, Unix seconds | Time of this response, not a release date |
data[].display_name, created_at | string | Claude groups only; created_at is the model's release date (RFC 3339) |
has_more, first_id and last_id are there for SDK compatibility only: the list is never paged, so do not page with them. Fields other than id can be missing for some models; treat unknown fields as optional.
Balance and quota
curl https://api.hop-base.com/v1/usage \
-H "Authorization: Bearer sk-your-key"{
"is_active": true,
"balance": 125.4,
"remaining": 125.4,
"unit": "USD",
"quota": {
"remaining": 125.4,
"api_key_remaining": 125.4,
"total": 0,
"used": 3.12,
"unlimited": true
}
}| Field | Type | Meaning |
|---|---|---|
balance | number | What this key can spend right now. Key without its own quota: the account balance. Key with a quota: the quota left on the key. Keys of team members and departments are further capped by the member's and department's remaining quota for the period |
remaining | number | Same value as balance |
is_active | boolean | true when balance is above 0 |
unit | string | Always "USD". It does not tell you the currency; see the note below |
quota.remaining | number | Same value as balance |
quota.api_key_remaining | number | Quota left on this key; equals the account balance when the key has no quota |
quota.total | number | The key's quota; 0 means no key quota |
quota.used | number | Total charged to this key so far |
quota.unlimited | boolean | true when the key has no quota of its own |
Amounts are in your balance currency
All amounts are in the currency your account balance is kept in — the same numbers the console shows for your balance and in Usage. Do not read the currency from unit.
A key with its own quota still draws on the account balance: when the account runs out first, requests return 402 even though balance here shows quota left.
This endpoint does not use the standard error body. Failures look like this:
| Case | Status | Body |
|---|---|---|
No key, or not an sk- key | 401 | {"is_active": false, "balance": 0, "message": "missing or invalid api key"} |
| Unknown or disabled key | 401 | "message": "invalid api key" |
| Expired key | 200 | "is_active": false, "message": "api key expired" |
| Team member disabled | 200 | "is_active": false, "message": "member disabled" |
Branch on is_active, not only on the HTTP status. For itemised charges, open Usage in the console.
Public model catalog
curl https://api.hop-base.com/api/v1/models/pricingNo key needed. The response allows cross-origin requests from any site and may be cached for up to 5 minutes (Cache-Control: public, max-age=300). Prices are the list prices in USD before your group's rate; what you actually pay per model is shown in the signed-in model catalog.
{
"code": 0,
"message": "ok",
"data": [
{
"platform": "openai",
"models": [
{
"id": "gpt-6-astra",
"name": "GPT-6 Astra",
"context_window": 1050000,
"capabilities": ["chat", "reasoning"],
"vendor": "openai",
"category": "chat",
"input": 10,
"cached_input": 1,
"output": 50,
"long_context": {
"threshold": 272000,
"input_multiplier": 2,
"cached_multiplier": 2,
"output_multiplier": 1.5
},
"price_unit": "token"
}
]
},
{
"platform": "minimax",
"models": [
{
"id": "speech-2.8-hd",
"name": "MiniMax Speech 2.8 HD",
"capabilities": ["tts"],
"vendor": "minimax",
"series": "minimax-speech",
"category": "audio",
"input": 100,
"output": 0,
"price_unit": "character"
}
]
}
]
}| Field | Type | Meaning |
|---|---|---|
code | integer | 0 on success |
data[].platform | string | The integration family the model is served through (for example openai, claude, gemini, kling). It is not the model maker — see vendor |
models[].id | string | Model ID to send |
models[].name | string | Display name |
models[].vendor | string | Model maker, for example openai, google; may be absent |
models[].series | string | Family used to group versions in the console; may be absent |
models[].category | string | chat, image, video, audio, or embedding |
models[].capabilities | string[] | For example chat, reasoning, image_generation, image_edit, video_generation, tts |
models[].context_window | integer, tokens | Absent when not published |
models[].price_unit | string | Unit of every price on the entry: token = per 1M tokens, second = per second of video, image = per image, character = per 1M billed characters |
models[].input, cached_input, output | number, USD per price_unit | cached_input is absent when caching does not apply. Speech models use input only |
models[].long_context | object | Present when a long-context tier exists: above threshold input tokens the whole request is billed at input_multiplier / cached_multiplier / output_multiplier |
models[].image | object | Per-image price by resolution tier, for example {"1k": …, "2k": …, "4k": …} |
models[].video_tokens | object | Video price per bucket (resolution, audio, reference media). The unit follows price_unit: per 1M video tokens for token, per second for second. The key name is historical |
A model can appear under more than one platform when it is offered through more than one group. New fields may be added; ignore fields you do not recognize.
Response headers
| Header | Sent on | Meaning |
|---|---|---|
x-request-id | Every response | ID of this request. Include it when you report a problem |
Retry-After | 429 from the concurrency cap; 429 and 503 when a wait time is known | Seconds to wait before retrying |
Retry-After-Ms | 429 from the concurrency cap, and other 429s with a known wait | The same wait in milliseconds |
HopBase does not send headers with your concurrency cap, requests in flight, or remaining balance. Use GET /v1/usage for the balance, and see Concurrency, timeouts, and billing for the caps. Some model responses carry their own rate-limit headers (x-ratelimit-*, anthropic-ratelimit-*); they do not describe your account's or key's limits, so do not throttle on them.