Credits and usage
How Direct API calls are charged, settled and refunded, how to quote a job first, and how to read your key's spend and ledger.
The Direct API charges Morphic credits from your organization's balance, the same pool Studio uses, and every response says how many.
- Direct API calls are charged at your organization's API rates, which can differ from what the same work costs in Studio. Every number the API gives you already reflects them:
plan_mediaquotes,estimated_credits, thebillingblock, generation reads, webhooks and/usage. - Rates are set per organization and are not published. Your account contact can share your API rate sheet.
- A rate change applies to generations started after it; a running generation is settled at the rate it started with.
- In Studio, under Settings > Credits, API spend appears as its own row, API, beside your members, and Split API by key breaks it down per key. It draws from the organization's credits but never from a member's allocation, and each key's credit limits cap it.
How a generation is charged
- At start, the generation is charged its quoted price. The start response reports it in
billing.credits_chargedandresult.estimated_credits. - Some outputs are priced by what is actually produced: speech and music by produced length, timeline renders by compute time. They carry
credits_settle_on_output_duration: true; the start charge is a floor, and the final amount is settled when the generation finishes. - A generation that ends
failedorcancelledis refunded in full, andbilling.credits_refundedreports the credits given back. A succeeded generation can also carry a partial refund, for example when a settled output came out shorter than the start charge covered. - Once a generation is final,
billing.credits_chargedis the net total on the ledger (start charge, plus or minus settlement, minuscredits_refunded) andcredits_are_finalistrue. A failed or cancelled generation ends withcredits_charged: 0andcredits_refundedequal to what it was charged.
| Field | Meaning |
|---|---|
estimated_credits | the price quoted and charged at start |
credits_charged | the net credits recorded for this generation so far, after settlement and refunds |
credits_refunded | every credit given back for this generation: the whole charge on failed or cancelled, any partial refund on a succeeded one; 0 when none, never negative |
credits_are_final | false while the generation runs or a settlement or refund is still due; true once nothing more will change it |
credits_settle_on_output_duration | true when the start charge is a floor settled from the produced output |
While a generation runs, credits_charged equals the start charge. Read it again once the generation is final for the settled amount; a webhook is sent only once it is.
Billing on each response
The billing block on a call's response reports what the call charged when it returned; Calling a tool lists it per kind of call. Refunds happen later, so they appear on the generation (GET /generations/{id} and /usage), never on the original response.
- A paid tool that answers in the call and whose work fails, such as
analyze_video, answers withcredits_charged: 0andcredits_refundedset to the refund. - The
Morphic-Credits-Charged: <integer>header carries the same number asbilling.credits_charged, except on an idempotent replay, which charges nothing more and sends0while its body repeats the original charge.
Quote before you spend
POST /v1/direct/tools/plan_media validates a plan of up to 20 steps and prices the steps it can, without starting or charging anything.
curl -s https://mcp.morphic.com/v1/direct/tools/plan_media \
-H "Authorization: Bearer $MORPHIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"steps": [
{ "id": "still", "tool_name": "generate_image", "arguments_json": "{\"prompt\":\"A red vintage bicycle\",\"aspect_ratio\":\"16:9\"}" },
{ "id": "clip", "tool_name": "generate_video", "arguments_json": "{\"prompt\":\"The bicycle rolls away\",\"duration_seconds\":8}", "depends_on": ["still"] }
]
}
}'- Leave out asset fields whose outputs do not exist yet, and name the step they come from in
depends_on. - Each step gets its
validation,quote_statusandestimated_credits. - The result also gives the
execution_order,known_estimated_credits,unquoted_step_idsandcomplete_estimate. Whencomplete_estimateisfalse, the sum is a lower bound, not a budget.
Key and balance
GET /v1/direct/me returns the calling key, its limits and spend, and the organization's balance. Any key can call it.
curl -s https://mcp.morphic.com/v1/direct/me \
-H "Authorization: Bearer $MORPHIC_API_KEY"{
"api_key": {
"id": "01928e00-…",
"name": "Order pipeline (prod)",
"scopes": ["assets:read", "assets:write", "generations:write", "projects:read"],
"expires_at": null,
"credit_limits": {
"total": { "limit": 500000, "used": 182340, "remaining": 317660 },
"monthly": { "limit": 100000, "used": 41200, "remaining": 58800, "resets_at": "2026-11-01T00:00:00.000Z" },
"daily": { "limit": null, "used": 2310, "remaining": null, "resets_at": "2026-10-26T00:00:00.000Z" }
}
},
"organization": {
"id": "01928d00-…",
"plan": "enterprise",
"remaining_credits": 1240000,
"unlimited": false
}
}Usage ledger
GET /v1/direct/usage (scope assets:read) returns every ledger entry the key caused, for reconciliation with your own records.
| Query | Meaning |
|---|---|
from, to | ISO 8601 bounds on created_at; default the last 30 days, at most 92 days |
generation_id | entries of one generation only |
limit | 1 to 200, default 50 |
cursor | the previous page's next_cursor |
curl -s "https://mcp.morphic.com/v1/direct/usage?from=2026-10-01T00:00:00Z&limit=200" \
-H "Authorization: Bearer $MORPHIC_API_KEY"{
"usage": [
{ "id": "0192a7b3-…", "created_at": "2026-10-25T09:14:03.500Z", "kind": "charge", "credits": 120, "generation_id": "0192a7b3-a1c4-…", "tool": "generate_video", "model_id": "mdl_7rk2pq9vxa4m", "metadata": { "order_id": "4411" } },
{ "id": "0192a7b4-…", "created_at": "2026-10-25T09:15:41.950Z", "kind": "settlement", "credits": -24, "generation_id": "0192a7b3-a1c4-…", "tool": "generate_video", "model_id": "mdl_7rk2pq9vxa4m", "metadata": { "order_id": "4411" } }
],
"totals": { "charged": 120, "settled": -24, "refunded": 0, "net": 96 },
"next_cursor": null
}kindis one ofcharge,settlementorrefund; a negativecreditsgives credits back.totalscovers the whole filtered range, not just the page.- A tool that answers in the call (
analyze_video,analyze_audio,filmlab_edit) starts no generation, so its entries carrygeneration_id: nulland are told apart bytool.
If you have any trouble or have any questions, feel free to reach out. We're happy to help. Write to us at support@morphic.com.