Gravity Feed API reference

Every Gravity Feed route, authentication, rate limits and QPS, idempotency, plan expiry and error codes.

This is the complete reference for the Gravity Feed REST API, version 1. It covers the base address, authentication, every route, rate limits, idempotency, plan freshness and error codes. The request and response fields are described in the data model.

Base address

ItemValue
Base URLhttps://spatial9.ai/api/gravity-feed/v1
FormatJSON over HTTPS
Schema version1.0
Maximum body65,536 bytes

Authentication

CallerHow to authenticateCounted as
DAW script, plugin or serverX-API-Key: s9gf_... or Authorization: Bearer s9gf_...rest
Spatial9 websiteYour signed-in sessionweb
MCP tool gravity_feed_planYour Spatial9 MCP sign-inmcp
A2A skill gravity_feed_planYour Spatial9 A2A sign-ina2a

Keys are created and revoked in the console. Only a hash of each key is stored.

Routes

Method and pathAuthUses limitPurpose
GET /capabilitiesNoneNoSchema version, limits, styles, shapes, effect kinds, transports
GET /profilesNoneNoGravity Profiles with parameters, ranges, OSC addresses and status
POST /validateNoneNoCheck a request body without planning
POST /plansRequiredYesCreate a plan
GET /usageRequiredNoYour last 30 days of requests and current allowance
GET /keysSigned-in sessionNoList your keys
POST /keysSigned-in sessionNoCreate a key, body { "name": "..." }
DELETE /keys/{id}Signed-in sessionNoRevoke a key

POST /validate

Returns { "valid": true|false, "schema_version": "1.0", "fields": [...] }. Each field error has path, code and message.

GET /usage

{
  "window_days": 30,
  "truncated": false,
  "rate_limit": { "sustained_qps": 1, "burst": 5, "available_now": 5 },
  "totals": { "last_24h": 12, "last_30d": 140, "succeeded": 138, "failed": 2,
              "by_transport": { "rest": 120, "web": 10, "mcp": 8, "a2a": 2 } },
  "latency_ms": { "p50": 2100, "p95": 4800 },
  "recent": []
}

latency_ms is null until you have a successful request. The numbers above illustrate the shape only.

Keys

GET /keys returns { "keys": [{ "id", "name", "key_prefix", "created_at", "last_used_at", "revoked_at" }] }. POST /keys returns { "key": {...}, "secret": "s9gf_...", "notice": "..." }; the secret is never shown again. DELETE /keys/{id} returns { "revoked": "<id>" }.

Rate limits and QPS

QPS means queries per second. One query is one POST /plans call, however many tracks it covers (up to 32) and however long the window (up to 128 beats).

PlanSustainedBurstMonthly volume
Free1 query per second5Unlimited for now
Higher volumeBy agreementBy agreementContact sales

The limit is a token bucket per account. It holds up to 5 tokens and refills at 1 per second. Every plan request uses one token, whether it arrives by REST, MCP, A2A or the website, and whichever key it uses. Creating more keys does not raise the limit.

Every plan response carries:

HeaderMeaning
X-RateLimit-LimitSustained queries per second
X-RateLimit-BurstBucket size
X-RateLimit-RemainingWhole tokens left right now
Retry-AfterOn 429, seconds to wait

Idempotency

request_id is your idempotency key. Sending the same request_id with the same body returns the stored result with Idempotent-Replayed: true and does not use a token. A different body under the same request_id returns 409 idempotency_conflict. While the first request is still running, a repeat returns 409 request_in_progress with Retry-After: 2.

Plan freshness

Plans are proposals. Each one records preconditions.session_id, preconditions.session_revision and an input hash, and expires 600 seconds after creation. An adapter must not write a plan when the session revision has changed or expires_at has passed; it requests a new plan instead.

Errors

Errors always use this shape:

{ "error": { "code": "invalid_request", "message": "The request did not match schema 1.0.", "fields": [] } }
StatusCodeWhat to do
400invalid_jsonSend valid JSON
401unauthorizedSend a valid key or sign in
404not_foundCheck the path, version or key id
405method_not_allowedUse the documented method
409idempotency_conflictUse a new request_id
409request_in_progressWait for Retry-After, then repeat
409key_limitRevoke a key before creating another
413payload_too_largeSend fewer tracks or shorter names
422invalid_requestFix the listed fields
429rate_limitedWait for Retry-After; contact sales for more
500internal_errorRetry later
502inference_failedRetry with the same request_id
503admission_unavailableWait for Retry-After, then repeat

FAQ

Does a validate call count against my limit?

No. /validate, /capabilities, /profiles, /usage and key management never use a token.

Do MCP and A2A have separate limits?

No. All four ways in share one bucket per account.

Go deeper on the blog