⚡ rapid.dance

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 unauthorizedNo Authorization: Bearer header and no __auth field in the body.
404 not_foundUnknown or revoked key, key does not belong to the account, or unknown query slug.
400 bad_requestBody is not a JSON object, or __auth is not a string.
413 too_largeBody exceeds the tier's input limit.
429 rate_limited / daily_limitPer-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.

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:

TierInput ≤Output tokens ≤Max time ≤Per request
S4 KB2561000 ms$0.0010
M4 KB10242000 ms$0.0030
L16 KB40965000 ms$0.010
XL16 KB1638430000 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