Skip to content
DecisionNodeDecisionNdedocs
  • Guides
  • API reference
  • Examples
  • Playground

start here

  • QuickstartGet startedGet an API key, send one request with three questions, and branch your code on the typed answers. Plain HTTPS, nothing to install.
  • POST /v1/decideAPI referenceAnswer typed questions about a state, optional images and video frames. One request, one buffered JSON response, one answer per question.
  • QuestionsConceptsQuestions say what to decide. Each one has a type that fixes the shape of its answer: a choice from your options, a score on your scale, a…
  • ConfidenceConceptsProbabilities are calibrated per question type, so a threshold means what it says on the data we measure.
  • ImagesConceptsSend up to 16 images and text in the same request. The model reads printed and handwritten text, amounts, dates, objects and layout, and…
  • Pricing and billingYou pay for input tokens only. Output is free because the model generates no text.
↑↓ moveopen6 suggestions
Get API keyGet API key
DecisionNodeDecisionNde

Get started

  • Introduction
  • Quickstart
  • CLI
  • Playground
  • Console and keys
  • With coding agents
  • MCP server
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Points and boxes
  • Images
  • Video
  • Boosters
  • Collections
  • Confidence
  • Determinism
  • API keys

Models

  • DecisionNode MoE
  • DecisionNode Flash
  • Limits
  • Versions
  • Dedicated capacity

Fine-tuning

  • Overview
  • Prepare your dataset
  • Upload and validation
  • Start a training run
  • Watch a run
  • The quality gate
  • Use your model
  • Limits and pricing

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Keys on devices
  • Bill your customers per device
  • Publishable keys

Integrations

  • Home Assistant
  • MQTT bridge
  • ROS 2

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • POST/v1/uploads
  • /v1/keys
  • GET/v1/keys/{key_id}/usage
  • GET/v1/usage
  • Errors
  • Safety check
  • Rate limits

Sessions API

  • Sessions overview
  • POSTOpen a session
  • WSStream frames
  • DELEnd a session

Collections API

  • Overview
  • POSTCreate a collection
  • GETList collections
  • GETRetrieve a collection
  • DELDelete a collection
  • POSTEnroll a face
  • GETList items
  • PATCHUpdate an item
  • DELDelete an item

Pricing and billing

  • Pricing and billing
  • Refer & earn

Policies

  • Responsible use
  • Data and privacy
  • Benchmarks
  • Pricing
  • Playground
Get API key
  • Guides
  • API reference
  • Examples
  • Playground

Get started

  • Introduction
  • Quickstart
  • CLI
  • Playground
  • Console and keys
  • With coding agents
  • MCP server
  • Examples

Concepts

  • State
  • Questions
  • Choice
  • Score
  • Truth
  • Number
  • Points and boxes
  • Images
  • Video
  • Boosters
  • Collections
  • Confidence
  • Determinism
  • API keys

Models

  • DecisionNode MoE
  • DecisionNode Flash
  • Limits
  • Versions
  • Dedicated capacity

Fine-tuning

  • Overview
  • Prepare your dataset
  • Upload and validation
  • Start a training run
  • Watch a run
  • The quality gate
  • Use your model
  • Limits and pricing

Patterns

  • Confidence-gated routing
  • Fan-out
  • Guardrails
  • Control loops
  • Keys on devices
  • Bill your customers per device
  • Publishable keys

Integrations

  • Home Assistant
  • MQTT bridge
  • ROS 2

API reference

  • Overview
  • POST/v1/decide
  • GET/v1/models
  • POST/v1/uploads
  • /v1/keys
  • GET/v1/keys/{key_id}/usage
  • GET/v1/usage
  • Errors
  • Safety check
  • Rate limits

Sessions API

  • Sessions overview
  • POSTOpen a session
  • WSStream frames
  • DELEnd a session

Collections API

  • Overview
  • POSTCreate a collection
  • GETList collections
  • GETRetrieve a collection
  • DELDelete a collection
  • POSTEnroll a face
  • GETList items
  • PATCHUpdate an item
  • DELDelete an item

Pricing and billing

  • Pricing and billing
  • Refer & earn

Policies

  • Responsible use
  • Data and privacy
  1. docs
  2. /
  3. API reference

Usage by key

The usage and cost of many keys over a period, one row per key, filtered by label, device or key: the read behind a monthly invoice run.

on this page6 sections
  1. Headers
  2. Query parameters
  3. Response
  4. Paging
  5. The cost
  6. as_of
  7. Which keys you can read
  8. Errors
  9. Limits

One row per key: each key's usage and cost over the period, for every key that matches your filters. Revoked and expired keys are included. For one key, use GET /v1/keys/{key_id}/usage. How to turn the rows into invoices is on Bill your customers per device.

GEThttps://api.decisionnode.com/v1/usage
curl -G https://api.decisionnode.com/v1/usage \
  -H "Authorization: Bearer $DECISIONNODE_MANAGE_KEY" \
  --data-urlencode "start=2026-10-01T00:00:00Z" \
  --data-urlencode "end=2026-11-01T00:00:00Z" \
  --data-urlencode "label=customer:acme"

Headers#

Authorizationheaderrequired
Bearer a Full access or Manage device keys key, on your server. A Decisions-only key gets 403 key_scope_denied, "This key may only make decisions.", and a Publishable key "A publishable key may only make decisions."

Query parameters#

startstringrequired
RFC 3339, with any offset, read in UTC and rounded down to the hour: the first moment counted.
endstringrequired
RFC 3339, rounded up to the hour: the first moment not counted, so a month is 2026-10-01T00:00:00Z to 2026-11-01T00:00:00Z. A period is at most 400 days. There is no limit on how far back it starts: usage is kept indefinitely.
granularitystringdefault "none"
none, hour, day or month: none answers the totals only; the others add buckets of an hour, a day or a calendar month, in UTC. hour takes a period of at most 7 days.
group_bystring
model or channel: adds by_model or by_channel, the totals split by model or by channel.
labelstring
name:value, such as customer:acme. Repeat it: a key must match every one, as on the keys list.
device_idstring
The key with this device_id: 1 to 128 printable ASCII characters.
key_idstring
One key, key_ and 32 hex digits; repeat it for up to 100.
scopestring
One kind: full, decide or keys.
statusstringdefault "all"
active, expired, revoked or all: what the key is now, not what it was in the period.
include_emptybooleandefault false
true lists the keys with no usage in the period too, each with totals of 0.
limitintegerdefault 100
Keys a page, 1 to 1,000; with a granularity other than none, at most 100. A page can hold fewer: see Paging.
cursorstring
The next_cursor of the page before.

Response#

200 OK with the period read, how current the figures are, and a page of rows, newest key first: pass next_cursor as cursor for the next page, until it is null. A key with no usage in the period has no row unless include_empty=true.

200 OK
{
  "period": {
    "start": "2026-10-01T00:00:00Z",
    "end": "2026-11-01T00:00:00Z"
  },
  "as_of": "2026-11-01T00:00:00Z",
  "items": [
    {
      "key": {
        "id": "key_3b8f0d6e2a914c7b9e5f1a2c4d6e8f01",
        "name": "Drone 0042",
        "scope": "decide",
        "device_id": "acme-drone-0042",
        "labels": { "customer": "acme", "plan": "pro" },
        "status": "active",
        "created_at": "2026-09-02T08:14:00Z",
        "revoked_at": null,
        "expires_at": null
      },
      "totals": {
        "requests": 12480,
        "free_tier_requests": 0,
        "input_tokens": 68640000,
        "image_tokens": 49920000,
        "video_tokens": 0,
        "booster_calls": 0,
        "cost": { "usd": 2.88288, "model_usd": 2.88288, "boosters_usd": 0 }
      }
    },
    {
      "key": {
        "id": "key_9d1c5e7a3b2f4e6d8c0a1b3e5f7a9c20",
        "name": "Drone 0017",
        "scope": "decide",
        "device_id": "acme-drone-0017",
        "labels": { "customer": "acme", "plan": "pro" },
        "status": "revoked",
        "created_at": "2026-08-11T10:02:00Z",
        "revoked_at": "2026-10-19T16:40:00Z",
        "expires_at": null
      },
      "totals": {
        "requests": 5210,
        "free_tier_requests": 0,
        "input_tokens": 28655000,
        "image_tokens": 20840000,
        "video_tokens": 0,
        "booster_calls": 0,
        "cost": { "usd": 1.20351, "model_usd": 1.20351, "boosters_usd": 0 }
      }
    }
  ],
  "next_cursor": null
}
periodobject
{"start", "end"} as applied: start rounded down and end up to the hour, in UTC.
as_ofstring
RFC 3339, never past period.end: the figures are complete up to here, and a call after it may not be counted yet. Once as_of equals period.end, the period's figures are final: see as_of.
itemsarray
One row per key.
keyobject
The key, as it is now: revoked and expired keys included.
id, name, scopestring
As on the key object.
device_idstring or null
null on a key without one.
labelsobject
{} on a key without any.
statusstring
active, expired or revoked.
created_at, revoked_at, expires_atstring or null
RFC 3339.
totalsobject
The key's totals for the period.
requestsinteger
Every call the key made, answered or not: decisions (refused and failed ones included) and session opens. A session's frames are not counted. Refused and failed calls cost nothing.
free_tier_requestsinteger
Of those, the requests whose model part the free tier covered, at no cost; their booster calls past the day's free booster calls are billed.
input_tokensinteger
Every input token, as each answer's usage.input_tokens counts it, image and video tokens included. A refused or failed decision's tokens count. There is no output count: output is free.
image_tokens, video_tokensinteger
The images' and the videos' shares of input_tokens.
booster_callsinteger
The booster calls billed, collection enrollments included.
costobject
{"usd", "model_usd", "boosters_usd"}, in USD, written as an answer's usage.cost is but in whole micro-dollars, so at most 6 decimals, and without its free_tier flag (free_tier_requests counts those calls): what your workspace is charged for these calls, usd = model_usd + boosters_usd. See the cost.
bucketsarray
With a granularity other than none: {"start", "end", "totals"} for each hour, day or month that has usage, oldest first, in UTC. The first and the last are cut to the period.
by_modelarray
With group_by=model: {"model", "totals"} for each model the key used, in name order.
by_channelarray
With group_by=channel: {"channel", "totals"} for all three channels, always, a quiet one at 0: live for decisions and every other call (collection enrollments and the free tier's requests included), session for sessions (an open counted as a request, its frames' tokens and cost included) and batch at 0.
next_cursorstring or null
The next page's cursor; null on the last page.

Paging#

Keys come newest first. A page reads at most 150,000 hourly rows: its keys, times the period's hours, times the models each key has used (counted to 16). So a page can hold fewer keys than limit: over a month, about 201 keys that each used one model, or 67 that used three; over 400 days, 15. It always holds at least one key. Follow next_cursor until it is null, and never stop at a short page.

The cost#

totals.cost is what your workspace is charged for the key's calls in the period: refused and failed calls, and the free tier's model part, at 0, and a capped key's answers at most what was reserved for them. On a prepaid workspace it is the list price as metered: what your balance was drawn down by, and your usage page's spend. On a workspace invoiced at a negotiated rate, it is the invoice's own arithmetic at the month's rate: summed over your keys, it matches the usage on your invoice, each answer's usage.cost, and your usage page's spend, which on such a workspace runs a couple of minutes behind. An invoice's workspace-wide lines (reserved capacity, collection storage, and the credit for usage drawn before invoicing began) are in no key's figures. Totals are priced per key, month and model, and each bucket and split on its own sums: at list prices they add up exactly; at a negotiated rate a part can differ from its share by a micro-dollar.

as_of#

  • Complete up to as_of. Usage is folded in every minute, from calls at least two minutes old, and as_of says how far: every call before it is counted. It never passes period.end.
  • Final once it equals period.end. For a calendar month that is normally a few minutes after 00:00 UTC on the 1st.
  • Nothing arrives late in a past hour. Usage is dated when it is recorded.
  • One exception, a rebuild. When past hours are rebuilt from their records, as_of steps back to the first of them until the rebuild is done, then comes forward again. Wait for it before you invoice.

Which keys you can read#

  • Your workspace's keys only. A Full access key reads every key made in its workspace, in the console or through /v1/keys, of any kind. A Manage device keys key reads the keys /v1/keys lists for it, the Decisions-only ones.
  • Any other id is 404 not_found_error, "Not found.", another workspace's key, a key that does not exist and an id that is not one alike, never 403, so a key id cannot be probed.
  • Revoked and expired keys stay readable, as their usage is kept indefinitely: billing a device after it is revoked is the usual case.
  • Always open: an empty balance never makes these routes 402, and a suspended workspace can still read its usage, to bill its own customers.

Errors#

  • 400

    Type
    api_usage_error
    When
    A query it cannot take, with one of: "start is an RFC 3339 time, such as 2026-10-01T00:00:00Z." (or end), "end is after start.", "A period is at most 400 days.", "granularity hour is for a period of at most 7 days; use day or month.", "granularity is none, hour, day or month.", "group_by is model or channel.", "start and end are times between the years 1 and 9999.", "Unknown query parameter: <name>.", "start and end are required.", "A label filter is name:value." (or "..., as labels are written."), "device_id is 1 to 128 printable ASCII characters, not starting with a space.", "key_id is a key's id, key_ and 32 hex digits.", "At most 100 key_id filters.", "scope is full, decide or keys.", "status is active, expired, revoked or all.", "include_empty is true or false.", "limit is a whole number from 1 to 1000.", "limit is at most 100 with a granularity other than none.", "cursor is a next_cursor this list gave."
    What to do
    Fix the query
  • 401

    Type
    authentication_error
    When
    The key is missing, unknown or revoked, or has expired (details.reason key_expired)
    What to do
    Check the Authorization header
  • 403

    Type
    key_scope_denied
    When
    A Decisions-only key, "This key may only make decisions.", or a Publishable key, "A publishable key may only make decisions."
    What to do
    Read usage from your server, with a Full access or Manage device keys key
  • 429

    Type
    rate_limit_error
    When
    More than 10 usage reads a second for your workspace, both routes together: "Rate limit reached. Retry after 1 s.", with Retry-After and retry-after-ms
    What to do
    Wait Retry-After, then retry
  • 529

    Type
    overloaded_error
    When
    Usage cannot be read right now, or this read waited more than a second behind your workspace's others, with Retry-After
    What to do
    Read one page at a time; wait Retry-After, then retry
Errors this call returns
StatusTypeWhenWhat to do
400api_usage_errorA query it cannot take, with one of: "start is an RFC 3339 time, such as 2026-10-01T00:00:00Z." (or end), "end is after start.", "A period is at most 400 days.", "granularity hour is for a period of at most 7 days; use day or month.", "granularity is none, hour, day or month.", "group_by is model or channel.", "start and end are times between the years 1 and 9999.", "Unknown query parameter: <name>.", "start and end are required.", "A label filter is name:value." (or "..., as labels are written."), "device_id is 1 to 128 printable ASCII characters, not starting with a space.", "key_id is a key's id, key_ and 32 hex digits.", "At most 100 key_id filters.", "scope is full, decide or keys.", "status is active, expired, revoked or all.", "include_empty is true or false.", "limit is a whole number from 1 to 1000.", "limit is at most 100 with a granularity other than none.", "cursor is a next_cursor this list gave."Fix the query
401authentication_errorThe key is missing, unknown or revoked, or has expired (details.reason key_expired)Check the Authorization header
403key_scope_deniedA Decisions-only key, "This key may only make decisions.", or a Publishable key, "A publishable key may only make decisions."Read usage from your server, with a Full access or Manage device keys key
429rate_limit_errorMore than 10 usage reads a second for your workspace, both routes together: "Rate limit reached. Retry after 1 s.", with Retry-After and retry-after-msWait Retry-After, then retry
529overloaded_errorUsage cannot be read right now, or this read waited more than a second behind your workspace's others, with Retry-AfterRead one page at a time; wait Retry-After, then retry

Limits#

  • Their own rate: 10 reads a second per workspace, both routes together, apart from your rate limits: reading usage never slows your decisions, and decisions never slow it. Over it, 429 rate_limit_error with Retry-After.
  • One read at a time: your workspace's usage reads are answered one after another, so reads sent in parallel queue, and one that waits more than a second is 529. Read pages in turn.
  • One call or many: read a month's invoice run with pages of /v1/usage, not a call per key.
  • Never cached: every answer carries Cache-Control: private, no-store, and x-decisionnode-request-id as every call does.
previousKey usagenextErrors

DecisionNode is built and run by Bynn Intelligence, Inc.

  • Home
  • Playground
  • Examples
  • Console
  • Responsible use
  • Terms
  • Acceptable use
  • Privacy
  • Data processing
  • Defence addendum
  • Cookies
  • Affiliate program

on this page

  1. Headers
  2. Query parameters
  3. Response
  4. Paging
  5. The cost
  6. as_of
  7. Which keys you can read
  8. Errors
  9. Limits