Skip to main content
API referenceBuyer endpoints

Buyer Endpoints

All require authentication (API key or session cookie).

Keys

Everything you can do to a key in the dashboard, you can do here over the API. Create, list, read, rename, revoke, and configure per-key routing preferences, with no browser. This is the same API the dashboard itself calls. Authenticate with any active buyer key (inf_…) or a session cookie; the tenant is resolved from your auth, never sent in the body.

Changing keys needs an unrestricted key or a session. A restricted key (one with a scope, trusted-only provider setting, budget, or expiry, or any team-workspace key) gets 403 restricted_key when it tries to create, rename, configure, or revoke a key, or to change /v1/buyer/preferences or /v1/buyer/providers. It can still list and read keys, and revoke itself.

POST/v1/buyer/keys

Create a key (up to 25 active per wallet). Requires an Idempotency-Key header. The inf_… secret is returned in the key field once. Store it immediately; afterwards only its id and key_prefix are recoverable.

GET/v1/buyer/keys

List keys, redacted (id, key_prefix, label, created_at, plus last_used_at / revoked_at when set). last_used_at is kept current as the key is used. Add ?workspace_id= to narrow a team org's keys to one workspace.

GET/v1/buyer/keys/{id}

Read one key's metadata (never the secret).

PATCH/v1/buyer/keys/{id}

Rename a key's label.

DELETE/v1/buyer/keys/{id}

Revoke a key. Irreversible; returns 204.

Keys
POST /v1/buyer/keys
Idempotency-Key: 9f8c1e2b-...     # required
{ "label": "Cursor" }
# → {
#     "id": "01JQ8G7YV3M4N5P6Q7R8S9T0AB",
#     "key": "inf_9f8c1e2b3a4d5e6f7a8b9c0d1e2f3a4b",   # shown once
#     "key_prefix": "inf_9f8c1e2b",
#     "label": "Cursor",
#     "created_at": 1713312000000
#   }

Per-key routing preferences

GET/v1/buyer/keys/{id}/preferences

Read a key's stored routing preferences and limits, plus secret_masking_available.

PUT/v1/buyer/keys/{id}/preferences

Configure a key. The body is { preferences, limits }; limits is reserved for future spend caps, so send {}.

preferences is merged, not replaced. A field you leave out keeps its stored value, null clears that one field, and any other value sets it. This call used to replace preferences wholesale, so { "preferences": {} } cleared everything; it now changes nothing. The same rule applies to PUT /v1/buyer/preferences and to PATCH /v1/keys/{id}, where provider_trust: "default" now clears only the trust setting and leaves mask_secrets as it was.

  • allow_untrusted (boolean, nullable): true lets this key route to providers outside the trusted-domain allowlist; null or not set inherits the account default (trusted-only).
  • mask_secrets (boolean, nullable, beta): true turns secret masking on for every request made with this key. false and null both leave it to the account. A key cannot turn off masking its account has turned on.

The response echoes the stored values and secret_masking_available: true when masking is available on the service, false when masking is paused on the service (a request that asks for it is then sent without masking). A write that turns mask_secrets on also returns secret_masking_effective_at, the ISO-8601 time from which it applies: about 31 seconds after the write, or about 70 seconds when masking has only just been turned on for the service. Send X-SI-Mask-Secrets: on to be covered before then.

Set the account-wide defaults with GET / PUT /v1/buyer/preferences: the same { preferences, limits } body, the same merge and the same two masking fields, with the account's stored values in preferences_account. A key without its own allow_untrusted inherits the account's, and an account mask_secrets: true covers every key.

If another write changes the same preferences at the same moment, the server re-reads and merges again; after three lost races it answers 409 concurrent_update, which is safe to retry.

Preferences
GET /v1/buyer/keys/{id}/preferences
# → { "preferences": { "allow_untrusted": true }, "limits": {},
#     "secret_masking_available": true }
Preferences inherit, they do not default to off
  1. Account preferenceYour baseline
  2. Key preferencenull = inherit
  3. Resolved valueWhat the request uses
A null on a key means 'inherit from the account', not 'disabled'. Setting it on the key is what overrides - which is why clearing a key preference restores the account value rather than turning the feature off.

mask_secrets is the one exception to that override. A key can turn masking on for itself, but a key's false does not override an account's true: a request is masked when its key or its account says so (or it sends X-SI-Mask-Secrets: on).

Profile

GET/v1/buyer/me

Balance, allowance, usage stats, per-model breakdown.

GET/v1/buyer/overview

Spend, savings and pace for a range in one call. range = 7d, 30d (default), or 90d, trailing and including today (UTC). Returns totals and previous (the equal range before it) with the same measures as /v1/analytics/spend/summary, plus kpis, daily (one entry per day with traffic), by_model (top models; 7 and 30 days only), distinct_models, avg_daily_spend_micro (your 7-day spending pace, integer micro-USD per day), cache_routing_mode, and computed_at. workspace_id picks a workspace of your org, or all for the org rollup (needs audit access on a team org); without it you get your active workspace.

coverage: false means the figures for that range aren't available yet. Every figure is then null or empty, never a zero you could mistake for no spend.

GET/v1/buyer/savings

Savings vs direct provider pricing (all-time). Add ?period=daily or ?period=weekly for savings bucketed by day or week.

cache_saved_usdc is what prompt caching saved you (integer micro-USD), counted from cache_saved_since (epoch ms), so show it as "since" that date rather than as a lifetime figure. It is signed per request and floored at zero on the total. Both are null until a request has been measured.

GET/v1/buyer/approve-status

Legacy on-chain allowance status. Not part of funding an account - use deposited USDC or fiat credits.

Profile
GET /v1/buyer/me

Usage Export

GET/v1/buyer/usage/export

CSV export of all usage records.

Columns: request_id, created_at, model, input_tokens, output_tokens, buyer_cost_usd, direct_cost_usd, settlement_status, tx_hash. Up to 50,000 rows.

Usage exportbash
GET /v1/buyer/usage/export?format=csv&from=2026-01-01&to=2026-04-13

Providers (BYOK)

Register your own provider keys as buyer-owned providers. A provider scoped to a specific model is tried first for that model (priority - the marketplace is overflow only). A provider with model: null is a catch-all fallback used for any model when all marketplace sellers fail.

POST/v1/buyer/providers

Register a priority provider for a specific model, or a catch-all fallback (used when all marketplace sellers fail).

GET/v1/buyer/providers

List providers. Each carries last_used_at (when the key last served a request, epoch ms) and uses_this_week (requests it served in the last 7 UTC days, today included). A provider added since these counters shipped reads uses_this_week: 0 until its first use; on an older one both are null until they can be stated in full, never a partial count.

GET/v1/buyer/providers/{id}

Read one provider (health, active, masked key hint, last_used_at, uses_this_week).

PATCH/v1/buyer/providers/{id}

Update a provider: rotate its api_key, change base_url or label, or enable/disable it with active.

DELETE/v1/buyer/providers/{id}

Remove a provider.

Routing order: model-scoped priority provider → marketplace sellers → catch-all (model: null) provider → universal fallback.

Providers (BYOK)
POST /v1/buyer/providers
{ "model": "claude-opus-4.6", "api_key": "sk-...", "base_url": "https://api.venice.ai/api/v1" }