API
Every query you define is an HTTP endpoint that turns a JSON input into a JSON object, using a fast LLM behind the scenes. Calls are designed to be fast enough to sit inline in your application's user flow, and to never leave your app hanging.
Call a query
POST https://api.rapid.dance/<account>/<query-slug>
Authorization: Bearer <api key>
{ …your input… }
curl -X POST https://api.rapid.dance/<account>/<query-slug> \
-H 'Authorization: Bearer <api key>' \
-d '{ …your input… }'
require "net/http"
require "json"
input = { ... } # your input object
res = Net::HTTP.post(URI("https://api.rapid.dance/<account>/<query-slug>"), input.to_json,
"Authorization" => "Bearer <api key>")
puts res["X-Rapid-Status"] # ok, or the failure kind
puts res.body
import requests
input = { ... } # your input object
res = requests.post("https://api.rapid.dance/<account>/<query-slug>",
headers={"Authorization": "Bearer <api key>"}, json=input)
print(res.headers["X-Rapid-Status"]) # ok, or the failure kind
print(res.json())
<?php
$input = [ /* your input */ ];
$ch = curl_init("https://api.rapid.dance/<account>/<query-slug>");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer <api key>"],
CURLOPT_POSTFIELDS => json_encode($input),
]);
$result = json_decode(curl_exec($ch), true);
const input = { /* your input */ };
const res = await fetch("https://api.rapid.dance/<account>/<query-slug>", {
method: "POST",
headers: { "Authorization": "Bearer <api key>" },
body: JSON.stringify(input),
});
console.log(res.headers.get("X-Rapid-Status")); // ok, or the failure kind
console.log(await res.json());
input := map[string]any{ /* your input */ }
body, _ := json.Marshal(input)
req, _ := http.NewRequest("POST", "https://api.rapid.dance/<account>/<query-slug>", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer <api key>")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
fmt.Println(res.Header.Get("X-Rapid-Status")) // ok, or the failure kind
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
The body must be a single JSON object. Its maximum size depends on the query's price tier (see Pricing). It is always parsed as JSON, so Content-Type is optional. It is passed to the model as data together with your saved instructions. The response is the output object exactly as your schema defines it:
HTTP/2 200 OK
Content-Type: application/json
X-Rapid-Status: ok
X-Rapid-Request-Id: req_…
X-Rapid-Latency-Ms: 121
{ …your output fields… }
Authentication without headers
If setting headers is awkward (a webhook, a no-code tool, a plain curl -d), put the key in the body instead as a top-level __auth field. It is removed before the input reaches the model and is never logged. The Authorization header wins when both are present.
POST https://api.rapid.dance/<account>/<query-slug>
{"__auth": "<api key>", …your input…}
Example
Say you run a deployment platform. A user has a few repositories connected and names a new deployment "blog". You want to guess which repo it's for to provide a slightly better first-time UX.
In rapid.dance you create a query called repo-selector with one line of instructions (pick the repo this deployment most likely belongs to; leave it blank if unsure) and one output field, selected_repo.
The API your code calls is then simply:
POST https://api.rapid.dance/demo/repo-selector
Authorization: Bearer rq_…
{
"repos": ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name": "blog"
}
curl -X POST https://api.rapid.dance/demo/repo-selector \
-H 'Authorization: Bearer rq_…' \
-d '{
"repos": ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name": "blog"
}'
{"selected_repo": "myexample/myblog"}
require "net/http"
require "json"
input = {
"repos" => ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name" => "blog"
}
res = Net::HTTP.post(URI("https://api.rapid.dance/demo/repo-selector"), input.to_json,
"Authorization" => "Bearer rq_…")
puts res["X-Rapid-Status"] # ok, or the failure kind
puts res.body # {"selected_repo" => "myexample/myblog"}
import requests
input = {
"repos": ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name": "blog"
}
res = requests.post("https://api.rapid.dance/demo/repo-selector",
headers={"Authorization": "Bearer rq_…"}, json=input)
print(res.headers["X-Rapid-Status"]) # ok, or the failure kind
print(res.json()) # {"selected_repo": "myexample/myblog"}
<?php
$input = [
"repos" => ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name" => "blog"
];
$ch = curl_init("https://api.rapid.dance/demo/repo-selector");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer rq_…"],
CURLOPT_POSTFIELDS => json_encode($input),
]);
$result = json_decode(curl_exec($ch), true);
// $result == ["selected_repo" => "myexample/myblog"]
const input = {
"repos": ["myexample/myblog", "myexample/waitlist-api"],
"deployment_name": "blog"
};
const res = await fetch("https://api.rapid.dance/demo/repo-selector", {
method: "POST",
headers: { "Authorization": "Bearer rq_…" },
body: JSON.stringify(input),
});
console.log(res.headers.get("X-Rapid-Status")); // ok, or the failure kind
console.log(await res.json()); // {"selected_repo": "myexample/myblog"}
input := map[string]any{
"repos": []string{"myexample/myblog", "myexample/waitlist-api"},
"deployment_name": "blog",
}
body, _ := json.Marshal(input)
req, _ := http.NewRequest("POST", "https://api.rapid.dance/demo/repo-selector", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rq_…")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
fmt.Println(res.Header.Get("X-Rapid-Status")) // ok, or the failure kind
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
// out == map[string]any{"selected_repo": "myexample/myblog"}
HTTP/2 200 OK
X-Rapid-Status: ok
X-Rapid-Latency-Ms: 121
{
"selected_repo": "myexample/myblog"
}
When the model answers but its answer is exactly the fallback object (it picked the "unsure" values for every field), the response is still 200 but carries X-Rapid-Status: empty instead of ok, so you can tell an empty answer from a real one.
Error handling
By default a query never blocks or breaks your app. If the model does not answer within max time, returns something that does not match the schema, the provider fails, or the query is disabled, you still get HTTP 200 with the query's fallback object (each field's default, or a zero value), plus headers telling you what happened:
HTTP/2 200 OK
X-Rapid-Status: timeout # timeout | invalid_output | provider_error | rate_limited | disabled
X-Rapid-Fallback: true
{"selected_repo": ""}
If you would rather see failures as errors, set the query's on failure option to "error" or send X-Rapid-Strict: 1. Then timeouts return 504, provider and output problems 502, and disabled queries 503, with a JSON error body.
Client errors
401 unauthorized | No Authorization: Bearer header and no __auth field in the body. |
404 not_found | Unknown or revoked key, key does not belong to the account, or unknown query slug. |
400 bad_request | Body is not a JSON object, or __auth is not a string. |
413 too_large | Body exceeds the tier's input limit. |
429 rate_limited / daily_limit | Per-key rate limit or the account's daily cap was hit. |
Error bodies look like {"error": {"code": "…", "message": "…", "request_id": "req_…"}}.
Additional telemetry info
Every response carries a few custom headers with telemetry about the call.
X-Rapid-Request-Id— find the call in your logs.X-Rapid-Status—ok,empty(the model answered with the fallback values), or the failure kind.X-Rapid-Latency-Ms— end-to-end time inside rapid.dance.X-Rapid-Fallback: true— present when the fallback object was returned.
Pricing
Requests are priced per call, not per token, so cost is predictable. You pick a tier per query; the tier bounds the input size, how many tokens the model may produce and the longest max time you can set:
| Tier | Input ≤ | Output tokens ≤ | Max time ≤ | Per request |
|---|---|---|---|---|
| S | 4 KB | 256 | 1000 ms | $0.0010 |
| M | 4 KB | 1024 | 2000 ms | $0.0030 |
| L | 16 KB | 4096 | 5000 ms | $0.010 |
| XL | 16 KB | 16384 | 30000 ms | $0.030 |
Calls that engage the model (ok, empty, timeout, invalid output) are billable. Client errors, rate limits, disabled queries and provider errors are not.
Good practice
- Keep API keys server-side; there is no CORS support on purpose.
- Treat the output as untrusted data from a model: validate anything that matters.
- Give every field a description and a sensible default so the fallback object is safe to use as-is.
- Start with the smallest tier and a max time of 1000 ms; check p95 latency on the dashboard and adjust.