Skip to content
Apier.no
Guides

Agent payments: discover prices, survive 402, top up, retry

The full prepaid-credit payment loop for autonomous agents: price discovery at /api/v1/pricing, the machine-readable 402 recovery contract, agent-initiated top-up requests with human approval, the agent payment rails (x402 and MPP) and how to read which are on, and balance visibility on every metered response.

[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-10-16]

Apier's company-data reads are metered against a prepaid credit balance held per API key. This guide is written for machine consumption first: exact field names, exact status codes, and complete example bodies, so an agent (or the developer wiring one) can implement the whole payment loop without guesswork. Every amount on this API is a whole-øre integer (100 øre = NOK 1); no floats, no fractional øre, ever.

While credit enforcement runs dark (the launch default), metered calls are not charged and a 402 is impossible; those responses carry the X-Credit-Check: shadow header instead. Everything below describes the live-enforcement contract, which is also what the enforcement.live field on /api/v1/pricing reports. That pricing response is served Cache-Control: no-store, so the flag is never stale out of a shared cache, but treat it as best-effort discovery: the authoritative charging signal is always the metered response itself (X-Credit-Check: shadow = not charged; X-Credits-* headers or a 402 body = enforcement live), never a previously fetched price list.

Two ways to pay — and they are independent

When billing goes live there are two mechanisms, and the most important thing to encode is that they do not interact.

  • A monthly subscription tier buys throughput and support. It sets your enforced rate limits (requests per minute, a stricter per-minute limit on company-data endpoints, and a per-organisation daily cap) plus your support level. It does not bundle an allowance of metered calls. There is no monthly included-call quota and no per-call overage.
  • Prepaid credits are a balance you top up, from which each metered company-data read draws its cost (50 øre for a company-data read). A credit balance does not raise any rate limit.

So there is no ordering question and nothing is "spent first": metered reads always draw from the credit balance, and the subscription always governs the ceilings. The consequences for an agent are concrete:

  • A zero credit balance means metered reads will 402, regardless of which subscription tier the account is on. Do not treat a paid subscription as a reason to keep calling.
  • Hitting a rate limit is a 429 with Retry-After, never a charge. Back off; topping up will not help.
  • Read your ceilings rather than guessing them. With your API key, read the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers on each rate-limited response (an unlimited Enterprise key gets none), and each tier's limits object (per_minute_category_a, per_minute_category_b, per_org_daily) from /pricing.json; null on a numeric field means unlimited, never zero. GET /api/v1/account/usage is the dashboard's session-only view: an API key gets 401 SESSION_REQUIRED there.

1. Discover prices before paying — GET /api/v1/pricing

GET https://www.apier.no/api/v1/pricing is public and keyless: an agent can price a workflow before it holds an API key or a balance. The response is derived from the canonical pricing configuration the 402 meter enforces; there is no second price table anywhere in the system. A previously fetched price can still become stale after a pricing change, so the per-call metered response (not a stored price list) remains the authoritative charging signal.

{
  "success": true,
  "data": {
    "schema_version": "1.0.0",
    "currency": "NOK",
    "credit_unit": "Whole øre (1 øre = NOK 0.01). Every cost, balance, and top-up amount on this API is a whole-øre integer — no floats, no fractional øre.",
    "metered_endpoints": [
      {
        "endpoint": "/api/v1/company/{org}/context",
        "method": "GET",
        "category": "rulebook_read",
        "cost_ore": 50,
        "mcp_tool": "get_company_context"
      }
    ],
    "enforcement": {
      "live": true,
      "authority_note": "Best-effort discovery signal. The authoritative charging signal is always the metered response itself: X-Credit-Check: shadow means the call was not charged; X-Credits-* headers or a 402 INSUFFICIENT_CREDITS body mean enforcement is live. Never infer charging behaviour from a previously fetched price list alone.",
      "insufficient_balance_status": 402,
      "insufficient_balance_error_code": "INSUFFICIENT_CREDITS",
      "recovery_contract_fields": ["retryable", "fix_hint", "docs_url", "top_up_url", "balance_ore", "cost_ore", "topup_request"]
    },
    "balance_visibility": {
      "rest_headers": ["X-Credits-Balance-Ore", "X-Credits-Cost-Ore", "X-Credits-Warning", "X-Credits-Warning-Threshold-Ore", "X-Credits-Topup-Url"],
      "mcp_metadata_field": "metadata.credits",
      "balance_endpoint": "/api/v1/account/credits/balance"
    },
    "low_balance_warning": {
      "factor": 5,
      "rule": "A warning is emitted when the post-debit balance is strictly below factor × the call's cost_ore."
    },
    "top_up": {
      "min_ore": 5000,
      "max_ore": 1000000,
      "top_up_url": "https://www.apier.no/dashboard/billing",
      "bounds_scope": "dashboard_checkout",
      "paths": {
        "dashboard_checkout": {
          "description": "A HUMAN completes a card payment on the billing dashboard. These are the bounds the checkout session validates.",
          "min_ore": 5000,
          "max_ore": 1000000,
          "top_up_url": "https://www.apier.no/dashboard/billing"
        },
        "agent_request": {
          "description": "An AGENT requests funding without leaving the API; a human then approves it on the billing dashboard and pays. The agent surface never touches a card. Same NOK 50 floor as the dashboard path, and the ceiling is an operator-adjustable server-side setting rather than a code constant.",
          "endpoint": "/api/v1/billing/topup-requests",
          "method": "POST",
          "amount_field": "amount_ore",
          "min_ore": 5000,
          "max_ore": null,
          "max_ore_source": "billing_settings.agent_topup_ceiling_ore",
          "max_ore_note": "Operator-adjustable; read live at both request and approval. The current value is carried on the 402 INSUFFICIENT_CREDITS body as topup_request.ceiling_ore."
        }
      }
    },
    "subscription_tiers": {
      "pricing_json_url": "https://www.apier.no/pricing.json",
      "pricing_page_url": "https://www.apier.no/pricing"
    },
    "how_to_pay_guide": "https://www.apier.no/docs/guides/agent-payments"
  }
}

Read top_up carefully: the two funding paths have different bounds. top_up.min_ore / top_up.max_ore are the dashboard checkout bounds (that is what bounds_scope tells you), and top_up.paths states each path explicitly. The agent top-up request path (§4b) shares the NOK 50 floor and its ceiling is null here on purpose: that ceiling is an operator-adjustable server-side setting, and a keyless cacheable surface must not advertise a value that may already have changed. The live ceiling reaches you on the 402 body as topup_request.ceiling_ore.

subscription_tiers points back at the other billing mechanism. This endpoint prices metered per-call reads only; tier prices and the rate limits a tier buys live at /pricing.json.

The list above is truncated to one metered endpoint for readability — the live response enumerates every metered endpoint. MCP tools inherit metering through their REST forward (one debit point), so the mcp_tool name next to each endpoint costs exactly the same as the endpoint itself, and every metered MCP tool's description also states its own cost.

2. Authenticate

Metered endpoints require an API key in the Authorization header:

# Replace {org} with a real organisasjonsnummer; 999999999 exists only in the sandbox.
curl -H "Authorization: Bearer apr_free_<your_key_here>" \
  https://www.apier.no/api/v1/company/{org}/context

A brand-new key has a balance of 0 øre, authoritatively zero rather than unknown. Check any key's own balance at any time (this read is deliberately scope-exempt and never debits):

curl -H "Authorization: Bearer apr_free_<your_key_here>" \
  https://www.apier.no/api/v1/account/credits/balance

3. Interpret the 402 — the machine-readable recovery contract

With an insufficient balance, a metered call returns HTTP 402 with error_code: "INSUFFICIENT_CREDITS". Nothing was charged: the balance is checked-and-debited atomically before the handler runs, so an insufficient balance leaves both the balance and the ledger untouched, deterministically.

A complete example 402 body:

{
  "success": false,
  "error_code": "INSUFFICIENT_CREDITS",
  "explanation": {
    "summary": "Forhåndsbetalt kredittsaldo er for lav for dette kallet.",
    "summary_en": "Prepaid credit balance is too low for this call.",
    "why": "Dette endepunktet koster 50 øre per kall, men saldoen på API-nøkkelen er 0 øre. Ingen belastning er gjort.",
    "why_en": "This endpoint costs 50 øre per call, but the API key's balance is 0 øre. Nothing was charged.",
    "fix_steps": [
      "Fyll på kredittsaldoen fra faktureringssiden (se top_up_url).",
      "Prøv forespørselen på nytt etter påfyll."
    ],
    "fix_steps_en": [
      "Top up the credit balance from the billing page (see top_up_url).",
      "Retry the request after topping up."
    ]
  },
  "retryable": false,
  "fix_hint": "Prepaid balance too low — top up (see top_up_url), then retry the request.",
  "fix_hint_en": "Prepaid balance too low — top up (see top_up_url), then retry the request.",
  "docs_url": "https://www.apier.no/docs/guides/agent-payments",
  "top_up_url": "https://www.apier.no/dashboard/billing",
  "balance_ore": 0,
  "cost_ore": 50,
  "topup_request": {
    "endpoint": "/api/v1/billing/topup-requests",
    "method": "POST",
    "amount_field": "amount_ore",
    "ceiling_ore": 100000
  },
  "_meta": {
    "rulebook_version": "1.0.0",
    "data_freshness": "2026-07-18T12:00:00.000Z",
    "last_verified": "2026-07-18T12:00:00.000Z",
    "source": "apier.no",
    "schema_version": "1.4.0",
    "response_timestamp": "2026-07-18T12:00:00.000Z",
    "response_hash": "sha256:…"
  }
}

How an agent should read it:

  • retryable: false: an identical retry with an unchanged balance fails identically. Do not loop.
  • balance_ore and cost_ore: compute the shortfall (cost_ore - balance_ore) and how much runway a given top-up buys.
  • top_up_url: the human handover. Topping up is a payment action; a human completes it on the billing dashboard. Surface this URL to your operator verbatim.
  • topup_request: the agent-initiated alternative. Instead of only handing over a URL, the agent can itself request a top-up of a specific amount at the embedded endpoint (see the next section). ceiling_ore is the current server-side cap on such requests, shown here at its launch value of 100000 øre (NOK 1,000, operator-adjustable). If it ever reads null, the ceiling could not be read at that instant; the request endpoint enforces the authoritative ceiling either way.
  • After the top-up lands, retry the identical request. Reads are side-effect free; the retry is safe.

The related failure 503 CREDIT_CHECK_UNAVAILABLE (credit infrastructure unreachable) is the opposite: retryable: true — wait a few seconds and retry the identical call. Its body states the actual money outcome in explanation.why_en.

The other 402: SPEND_CAP_REACHED

A key's owner can cap what that key may spend on metered reads per Europe/Oslo calendar day and per Europe/Oslo calendar month (see Spend caps below). A call that would take the key's spend in the current day or month past a cap returns HTTP 402 with error_code: "SPEND_CAP_REACHED". It is checked inside the same atomic debit step, before the debit, so it charges nothing and writes no ledger row.

The machine fields, beside the usual envelope:

{
  "success": false,
  "error_code": "SPEND_CAP_REACHED",
  "retryable": true,
  "cap": "daily",
  "cap_ore": 2000,
  "spent_ore": 2000,
  "cost_ore": 50,
  "resets_at": "2026-10-13T00:00:00+02:00"
}

How an agent should read it:

  • Branch on error_code: this is not a balance problem. There is no top_up_url, because topping up does not lift a cap.
  • retryable: true: the identical call succeeds after resets_at (the next Oslo midnight for daily, the first instant of the next Oslo month for monthly). The Retry-After header carries the same moment as a delay in seconds. Wait; do not loop before then.
  • To continue sooner, a human who owns the key raises or clears the cap.
  • When both caps would be exceeded, cap is monthly: the call cannot succeed before the month resets.

4. Top up — the dashboard checkout path

Top-ups are made by a human on the billing dashboard at the exact top_up_url the 402 body carries (byte-identical to the one /api/v1/pricing and /api/v1/account/credits/balance advertise).

Bounds for THIS path (dashboard checkout, human pays by card): min_ore: 5000 (NOK 50) to max_ore: 1000000 (NOK 10,000) per transaction. These are the top_up.min_ore / top_up.max_ore values on /api/v1/pricing, labelled there by top_up.bounds_scope: "dashboard_checkout". They are not the bounds on the agent-request path in §4b, which shares the floor but has a live, operator-adjustable ceiling — do not validate an amount_ore against the wrong set.

Credits are granted exactly once per completed payment — replayed payment events are deduplicated server-side.

4b. Or request a top-up as the agent — the agent-request path

An agent can also request a top-up itself, without leaving the API. The request creates a pending review item: a human approves it, and a human pays. The agent surface never touches the payment. In detail:

curl -X POST \
  -H "Authorization: Bearer apr_free_<your_key_here>" \
  -H "Content-Type: application/json" \
  -d '{"amount_ore": 10000}' \
  https://www.apier.no/api/v1/billing/topup-requests
{
  "success": true,
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "amount_ore": 10000,
    "status": "pending",
    "created_at": "2026-07-23T09:00:00.000Z",
    "expires_at": "2026-07-24T09:00:00.000Z",
    "approval_url": "https://www.apier.no/dashboard/billing"
  }
}

The rules an agent can rely on:

  • Any valid key may request. No scope is required — a key that just hit 402 can always ask for funding. The request funds the calling key only; the amount is the only body field.
  • Floor: 5000 øre (NOK 50). The same floor as a direct top-up on the billing dashboard. Below it, card fees eat the top-up; you get 400 TOPUP_BELOW_MINIMUM with requested_ore + minimum_ore. A request created before the floor rose to NOK 50 can no longer be approved: approving it answers 409 TOPUP_BELOW_MINIMUM, the human declines it instead, and the agent creates a new request of at least NOK 50.
  • Ceiling: adjustable, 100000 øre (NOK 1,000) at launch. The ceiling is a server-side setting read live at BOTH request creation and human approval; lowering it takes effect on requests that are already pending. Over the ceiling you get 400 TOPUP_CEILING_EXCEEDED with requested_ore + ceiling_ore and both remedies: lower the amount, or hand over to your operator, who can top up without any ceiling at the dashboard.
  • A human decides. The request appears on the operator's billing dashboard (approval_url). Approval opens the same Stripe-hosted checkout as a direct top-up, which the human completes (or abandons) there; decline ends the request with no payment surface ever touched. Nothing is charged by the request itself, in any mode.
  • Expiry: 24 hours. An unresolved request expires; simply request again. At most 3 pending requests per account and 5 requests per hour per key.
  • Idempotency: send an Idempotency-Key header to make retries safe; a replay returns the stored response instead of creating a second request.

After the human approves and completes payment, credits land on the key's balance exactly as for a direct top-up (deduplicated, granted once); poll /api/v1/account/credits/balance or retry the original metered call.

4c. Agent payment rails: x402 and MPP (switched off today)

The two paths above both end with a human paying. Apier is adding a third door that a headless agent can walk through on its own: paying for a top-up over an open machine-payment protocol, settled by Stripe. No agent payment rail is switched on yet. Each rail ships behind its own server-side switch, off by default, and is turned on only after a founder check. Never assume a rail is live: read it.

Read what is on now: GET /api/v1/payments/options

GET https://www.apier.no/api/v1/payments/options is public and keyless. It lists only the methods whose rail is switched on, so today methods is an empty list and agent_payments_enabled is false:

{
  "success": true,
  "data": {
    "schema_version": "1.0.0",
    "currency": "NOK",
    "agent_payments_enabled": false,
    "methods": [],
    "topup": { "min_ore": 5000, "max_ore": 500000 },
    "pay_to_get_key_enabled": false,
    "pay_per_request_enabled": false,
    "terms": { "url": "https://www.apier.no/terms", "version": "2026-10-16" },
    "links": {
      "pricing": "/api/v1/pricing",
      "topup_status": "/api/v1/payments/topups/{payment_intent_id}",
      "guide": "https://www.apier.no/docs/guides/agent-payments"
    }
  }
}

The example above is shortened; the live response also carries note fields and _meta. While a rail is on, the 402 INSUFFICIENT_CREDITS body gains a payment_offer object (options_url, methods, min_topup_ore, max_topup_ore) built by the same code, so the two never disagree. While every rail is off the 402 body has no payment_offer at all.

The rails, and which come first

MethodProtocolNetwork and assetStatus
Stablecoinx402Base, USDCComing: the first agent rail
StablecoinMPPTempo, USDC.eComing: the first agent rail
StablecoinMPPSolana, USDCComing: the first agent rail
Card through a Shared Payment TokenMPPnone (charged in NOK)Not available: Shared Payment Tokens are not enabled for this account yet

Each method in methods carries protocol, rail, payment_method, network and asset. A later protocol is added as a new method value; no existing field changes meaning.

The rules an agent can rely on once a rail is on

  • Floor and ceiling. An agent top-up is at least 5000 øre (NOK 50, the same floor as every other top-up) and at most 500000 øre (NOK 5,000) per payment. Read both from topup.min_ore and topup.max_ore.
  • NOK ledger. Whatever the agent pays in, the balance is credited with the gross NOK amount Stripe records for the payment, in whole øre, once per payment. Apier never holds crypto, never runs a wallet and never reads chain data to credit a balance.
  • Refunds. Only unused credit is refundable.
  • Terms. Paying through an agent rail counts as acceptance of the terms on behalf of the agent's owner. The 402 offer does not carry the terms itself: follow its options_url and read terms.url and terms.version from the options response, which name the terms that apply at the time of payment.
  • Spend caps are not a payment wall. SPEND_CAP_REACHED never carries a payment offer: paying more cannot lift a cap the owner set. Sandbox routes, the public sandbox mirror, sandbox-bearer MCP calls and free keyless tools never ask for money either.

Planned flows, not yet built

  • Top-up of an existing key's balance: the first flow, for the stablecoin rails above.
  • Pay to get a key: coming. An agent with no account pays, and receives an account, one API key and the credit in one loop. Such an account can only read data and make paid lookups until a person takes it over by email, and without a contact email its balance cannot be recovered if the key is lost.
  • Pay per request: coming later. No account, no key, no wallet: pay for exactly one call.

What is not supported

AP2 mandates, direct wallet transfers to Apier, and Vipps or Stripe Checkout as agent rails. Stripe Checkout (the dashboard path in §4) is for a human paying by card; Vipps, when it comes, is a human rail too.

Sources

The protocol facts above come from Stripe's own pages, read on 2026-10-16: the Stripe blog post "Introducing the Machine Payments Protocol" (18 March 2026) and the Stripe documentation page "Machine payments". The second lists the three stablecoin network and asset pairs in the table, a 0.01 USDC minimum for stablecoin payments, refunds through the Refunds API, and stablecoin access outside the US by request. The public evidence summary, with links, is on AI agents.

5. Retry and read your balance on every success

After topping up, the identical request succeeds. While enforcement is live, every successful metered response reports what it cost and what is left, twice over from the same debit: in whole øre on the X-Credits-* response headers, and in NOK on the body's _meta. While enforcement runs dark the X-Credits-* headers are not sent at all: the response carries X-Credit-Check: shadow and the body's _meta says cost_nok: 0, wallet_remaining_nok: null.

HTTP/1.1 200 OK
X-Credits-Balance-Ore: 950
X-Credits-Cost-Ore: 50
X-Correlation-ID: 550e8400-e29b-41d4-a716-446655440000
{
  "success": true,
  "data": { "...": "..." },
  "_meta": {
    "cost_nok": 0.5,
    "wallet_remaining_nok": 9.5,
    "response_hash": "sha256:..."
  }
}
  • _meta.cost_nok is what this call debited, in NOK as a JSON number rendered from the integer-øre ledger (50 øre is 0.5; the ledger itself never leaves whole øre). It is 0 when nothing was charged: while enforcement runs dark (X-Credit-Check: shadow) and on every sandbox response.
  • _meta.wallet_remaining_nok is the balance left after the debit, or null when no balance was read (shadow mode, sandbox). Divide it by the per-call price from GET /api/v1/pricing to know how many reads remain before a 402.
  • On the metered production routes both fields are present only on the 2xx body. A production error envelope carries neither: a refunded 5xx/403/404 claims no cost rather than a wrong one. Not every error is refunded, though: a 400 the route returns after the debit (a validation failure on input it had to read) stays charged like any other processed request, and it carries neither the two fields nor the X-Credits-* headers, so that charge is visible only in the ledger. Unmetered endpoints never carry them. The sandbox is the one surface that carries the pair on every response, success and error alike, always 0 / null: nothing on the sandbox can ever cost anything, so the constant is never a wrong claim.
  • The data block is never touched. The fields live under _meta, which the provenance response_hash excludes by design, so the receipt still verifies: remove the top-level _meta from the JSON you received, canonicalise, hash with SHA-256 and compare against response_hash (hashing the raw bytes including _meta never matched, before or after this change).
  • The ETag identifies the data representation, not the billing metadata. It is computed before the credit layer stamps the two fields, exactly as it already excludes response_timestamp, response_time_ms and cache_age_ms, so a conditional request can still short-circuit to a (refunded) 304. Treat cost_nok and wallet_remaining_nok in a body you cached as describing the call that produced it: a later 304 means the data is unchanged and that revalidation cost nothing; it says nothing about your current balance. Read the balance from a fresh 2xx (or GET /api/v1/account/credits/balance) when you need it now.

Retrying a metered POST. POST /api/v1/brreg/company-profile is the one metered POST, and it honours the Idempotency-Key header: a retry carrying the same key and body is answered from the cached first response with Idempotent-Replay: true and is not debited again. The replay short-circuits before the credit check, so the ledger shows one debit for the pair and the body's _meta.cost_nok and wallet_remaining_nok, like the X-Credits-* headers, are the first call's values: a replay echoes the balance as it stood after that first debit, not your balance now. The metered GET reads take no Idempotency-Key (the header is scoped to write-shaped endpoints), so a repeated GET is a second metered read.

When the post-debit balance drops strictly below 5× the call's cost, a low-balance warning trio rides along, the agent's cue to request a top-up before the next 402:

HTTP/1.1 200 OK
X-Credits-Balance-Ore: 200
X-Credits-Cost-Ore: 50
X-Credits-Warning: low_balance
X-Credits-Warning-Threshold-Ore: 250
X-Credits-Topup-Url: https://www.apier.no/dashboard/billing

6. The same contract over MCP

MCP tools forward to the REST endpoints, so the money semantics are identical: one debit point, one price. The envelope surfaces:

  • Success: metadata.credits on the tool result, { "balance_ore": 200, "cost_ore": 50, "balance_warning": { "threshold_ore": 250, "remaining_ore": 200, "top_up_url": "https://www.apier.no/dashboard/billing" } } (balance_warning present only below the threshold), and the NOK pair on the envelope's _meta: { "cost_nok": 0.5, "wallet_remaining_nok": 2 }, relayed verbatim from the REST body so the two surfaces can never disagree (0 / null when nothing was charged).
  • Insufficient balance: metadata.error_code: "INSUFFICIENT_CREDITS" with metadata.retryable: false, metadata.remediation, and the same recovery fields: metadata.top_up_url, metadata.balance_ore, metadata.cost_ore.
  • Spend cap reached: metadata.error_code: "SPEND_CAP_REACHED" with metadata.retryable: true, metadata.cap, metadata.cap_ore, metadata.spent_ore, metadata.cost_ore, metadata.resets_at and metadata.retry_after_seconds. On a capped key the success envelope's _meta also carries spend_cap_remaining_nok.

Spend caps per key

The owner of a key can set a daily cap, a monthly cap, or both, in whole øre, while signed in to the dashboard:

PATCH /api/v1/account/keys/{id}
Content-Type: application/json

{ "daily_cap_ore": 2000, "monthly_cap_ore": 30000 }
  • A field left out keeps its stored value; null clears that cap; 0 refuses every metered call on the key (a freeze).
  • Days and months are Europe/Oslo calendar days and months, so a daily cap resets at Oslo midnight, including across daylight-saving changes.
  • Spend is the key's metered debits in the window. Refunded calls (5xx, 403, 404, 304) do not count, and a retried call answered from the Idempotency-Key cache was never debited a second time, so it does not count twice either.
  • Caps apply only while credit enforcement is live. In shadow mode nothing is debited, so nothing is counted.
  • Every successful metered response on a capped key carries _meta.spend_cap_remaining_nok: what is left under the tighter cap after this call, in NOK.
  • The endpoint takes the dashboard session, not an API key, so an agent cannot raise its own cap.

Charging rules an agent can rely on

  • A 402 never charges (checked-and-debited atomically before execution).
  • A successful call (2xx) charges exactly its advertised cost_ore.
  • Outcomes where no billable work was delivered (any 5xx, 403 (scope rejection), 404 (unknown company), 304 (revalidation hit)) are automatically refunded; the ledger nets to zero for those calls.
  • Same input, same price: pricing is configuration, not negotiation (Rule 9 determinism extends to the meter).

When a subscription lapses

A failed renewal does not cut an agent off at the moment the card declines, and it does not cut it off silently either. This section describes what an agent actually observes, and what it can and cannot fix on its own.

A failed renewal buys a grace window, not an instant cutoff. When a renewal charge fails, Stripe moves the subscription to past_due. The paid tier is kept for 7 days measured from current_period_end; a card blip must not instantly drop a paying customer to free limits. The boundary is exclusive: at exactly current_period_end + 7 days the paid access ends. Stripe's own dunning retries usually resolve the state inside that window.

The downgrade is not instantaneous when the window closes. Grace expiry is a clock event, not a Stripe event; no webhook fires at the moment it lapses. A daily reconciliation job compares every subscription against Stripe and applies the correction, so expect the downgrade to land within about a day of the window closing rather than the second it does. Design for the transition being observable, not for a precise timestamp.

Cancellation takes effect at the end of the current billing period. The billing portal schedules the cancellation for the period end, you keep the tier you have paid for until then, and the consumer moves to the free tier when Apier processes the cancellation event at that point. A cancelled or expired subscription is never in grace.

API keys are never revoked for billing reasons. A downgrade changes the ceiling, not the credential; no key is revoked, disabled, or rotated when a subscription lapses. Calls that fit inside the free-tier limits keep working with the same Authorization header.

What changes is the rate limit, and the symptom is a 429. On downgrade the per-minute Category A and Category B limits and the per-organisation daily limit all drop to their free-tier values. An agent that was comfortably inside its paid ceiling will start collecting 429 RATE_LIMIT_EXCEEDED responses carrying a Retry-After header. Treat a sudden onset of 429s on unchanged traffic as a possible billing signal, not only as a traffic-shaping problem.

Read your own ceilings instead of inferring them. An agent holding an API key reads the X-RateLimit-* headers on each response and the per-tier limits objects in /pricing.json; the account owner, signed in to the dashboard, sees the full picture through GET /api/v1/account/usage (session-only: an API key gets 401 SESSION_REQUIRED), which returns a limits object alongside the usage counts:

{
  "tier": "starter",
  "per_minute_category_a": 300,
  "per_minute_category_b": 150,
  "per_org_daily": 5000,
  "window_seconds": 60,
  "subscription_status": "past_due"
}

null on any numeric field means unlimited (the enterprise tier), never zero calls. A null limits object means the tier could not be resolved; treat it as unknown and back off conservatively; it is deliberately not defaulted to the free-tier numbers. Note that subscription_status is diagnostic only: tier is what the limiter enforces, and a past_due status does not by itself mean the tier has already dropped — during the grace window the status reads past_due while tier is still the paid one.

The fix is a human handover. Restoring a lapsed subscription means updating a payment method, which an agent must never attempt. The billing portal at https://www.apier.no/dashboard/billing is where a human does it; the account owner is also emailed when a downgrade is applied. This is the same handover boundary as a top-up above a ceiling: surface it, do not work around it.

Prepaid credits are a separate ledger. A subscription lapse does not consume, void, or refund a prepaid credit balance. The two mechanisms are independent: the subscription sets rate-limit ceilings, credits pay for metered calls.

Building with an LLM? Read llms.txt for agent-oriented integration guidance.