Base URL: https://json.flyingfishspace.com
Last Updated: September 30, 2026
Pass your API key in the
X-API-Key
header on every programmatic request. No login, no OAuth, no cookies.
Check your current credit balance and key status. Useful for scripts and integrations that need to confirm a key is still active before making billed calls, without spending a credit on a real processing request.
curl https://json.flyingfishspace.com/api/v1/status \ -H "X-API-Key: ffs_live_YOUR_KEY"
Response (200 — success)
{
"key": "ffs_live_YOUR_KEY",
"balance": 97,
"status": "active",
"platform_uuid": "8f14e45f-...-9d3c",
"module_id": "json_pipeline"
}
Response (401 — invalid key)
{ "error": "Invalid API key" }
status is "active" or "revoked"; a
revoked key means you've reissued via /billing and must switch
to the new key value.
Every tool on this platform shares the same underlying safety principle: reject rather than silently corrupt. If we can't guarantee a repair is accurate and complete, we return a clear error instead of a plausible-looking but wrong result.
JSON's number type has two independent precision ceilings, and this
platform enforces both the same way: reject before parsing, rather than return a
silently-corrupted value. Every endpoint on this platform (clean,
schema, transform, and ai-clean) scans for
both before any parsing happens, and rejects outright (422, not charged) if either
is violated.
Decimal precision: JSON cannot exactly represent more than ~15 significant digits — most parsers silently round anything beyond that, with no warning. Encode high-precision decimals (financial amounts, coordinates, scientific values) as strings instead.
Large-integer precision: integers exceeding
Number.MAX_SAFE_INTEGER (253-1, 16 digits) — snowflake IDs,
64-bit database keys, and similar — lose precision the moment they're parsed into a
JS Number, the same way an oversized decimal does. There is no code path that can
"fix" this after the fact; it must be caught before parsing. Encode large integers
as strings instead.
Underflow: a non-zero decimal too small for a 64-bit float (below about
2.47×10-324, e.g. 1e-400) would silently become 0
when parsed, so it is rejected the same way (422, not charged). Tiny values that are still
representable are accepted. When a request contains many violations, only the first 100 are
listed.
Note for transform's csvToJson operation:
the CSV text you submit is itself a JSON string field, so numbers inside it are
checked separately from the surrounding request — both precision ceilings above
apply equally to values inside the CSV.
When ai-clean repairs your data, it's only permitted to fix structure,
keys, and — when your schema declares an enum — remap semantically
non-conforming values. Any other field the AI tries to silently alter gets the whole
repair rejected (422; a flat 1 credit is charged because the model already ran — see
the billing summary in the AI-Assisted JSON Repair section). The AI also can't invent a value for a required
field that was genuinely missing from your input; a missing field must surface as
missing, not as a fabricated placeholder.
Known limitation: value-lock compares the AI output against a baseline
of your original data, taken from the rule-based repair or a direct parse of
rawInput. If neither can parse your input (typical for severely malformed
text), no baseline exists and value-lock cannot run for that request. The JSON-validity
check and the schema check (if you supplied a schema) still apply. This is a known
limitation and has not been resolved yet.
ai-clean caps input at 3072 bytes — well below the platform-wide 1MB
limit. This number isn't arbitrary: it comes from real batch testing showing that
larger inputs carry a measurable risk of the model silently truncating or altering
content mid-generation. Oversized input is rejected before any AI call is made
(413, not charged) instead of risking an incomplete or subtly incorrect repair.
schema is also size-capped for this endpoint (currently 8192 bytes, a
provisional value that may change) and complexity-checked before any AI call; failures
return 413 or 422 and are never charged.
Submit raw or broken JSON for repair. The engine runs up to 11 repair stages
and returns the cleaned object. Charged 1 credit on success only. If your text has prose
around the JSON, the first complete, parseable {...} or [...]
block is extracted; input that is already valid JSON is never altered by that step.
repairs lists at most the first 100 repairs; repairs_total is
the exact number performed and repairs_truncated is true when
the list was cut.
curl -X POST https://json.flyingfishspace.com/api/json/v1/clean \
-H "X-API-Key: ffs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "age": 30,}'
Response (200 — success)
{
"success": true,
"data": { "name": "Alice", "age": 30 },
"repairs": ["Removed trailing comma before '}'"],
"repairs_total": 1,
"repairs_truncated": false,
"remaining_balance": 99
}
Response (200 — unrecoverable)
{
"success": false,
"error": "JSON structure could not be repaired.",
"repairs": ["Removed UTF-8 BOM"],
"repairs_total": 1,
"repairs_truncated": false,
"remaining_balance": 99
}
Response (422 — numeric precision would be lost, not charged)
{
"success": false,
"error": "Input contains numeric literal(s) exceeding safe precision (max 15 significant digits): \"1234567890.123456\" (16 significant digits). Encode high-precision numbers as strings instead (e.g. \"amount\": \"1234567890.123456789012\") to preserve exact values.",
"repairs": []
}
Response (422 — large-integer precision would be lost, not charged)
{
"success": false,
"error": "Input contains integer literal(s) exceeding safe precision (max 16 digits, Number.MAX_SAFE_INTEGER = 9007199254740991): \"22233344455566677788\" (20 digits). Encode large integers as strings instead (e.g. \"id\": \"22233344455566677788\") to preserve exact values."
}
number type cannot exactly represent more than ~15 significant
digits — most parsers, including this one's underlying engine, silently round anything
beyond that. Rather than return a rounded value with no warning, this endpoint rejects
the request outright. If you need to preserve exact high-precision values (financial
amounts, coordinates, scientific data), encode them as JSON strings instead of numbers.
Flatten, unflatten, rename keys, or convert between JSON and CSV. Charged 3 credits on success only.
curl -X POST https://json.flyingfishspace.com/api/json/v1/transform \
-H "X-API-Key: ffs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"operation": "flatten", "input": {"user": {"name": "Alice"}}}'
Body parameters
Response (200 — success)
{
"success": true,
"data": { "user.name": "Alice" },
"remaining_balance": 97
}
Response (422 — numeric precision would be lost, not charged)
{
"success": false,
"error": "Input contains numeric literal(s) exceeding safe precision (max 15 significant digits): \"1234567890.123456\" (16 significant digits). Encode high-precision numbers as strings instead (e.g. \"amount\": \"1234567890.123456789012\") to preserve exact values."
}
number type cannot exactly represent more than ~15 significant
digits — most parsers, including this one's underlying engine, silently round anything
beyond that. Rather than return a rounded value with no warning, this endpoint rejects
the request outright. If you need to preserve exact high-precision values (financial
amounts, coordinates, scientific data), encode them as JSON strings instead of numbers.
Response (422 — large-integer precision would be lost, not charged)
{
"success": false,
"error": "Input contains integer literal(s) exceeding safe precision (max 16 digits, Number.MAX_SAFE_INTEGER = 9007199254740991): \"22233344455566677788\" (20 digits). Encode large integers as strings instead (e.g. \"id\": \"22233344455566677788\") to preserve exact values."
}
Response (422 — precision violation inside CSV text for csvToJson, not charged)
{
"success": false,
"error": "CSV input contains integer value(s) exceeding safe precision (max 16 digits, Number.MAX_SAFE_INTEGER = 9007199254740991): \"22233344455566677788\" (20 digits). Encode large integers as quoted strings in the CSV instead to preserve exact values."
}
csvToJson, both precision checks run twice: once against the
overall request body, and once against the raw CSV text in input
specifically — the CSV text is itself a JSON string field, so numeric values
inside it need a separate scan. Errors from this second scan are prefixed
"CSV input contains..." to distinguish them from a violation in the outer
request structure.
Response (422 — key collision, not charged)
{
"success": false,
"error": "flattenObject: path collision at \"a.b\" — two different input paths flatten to the same key."
}
flatten and jsonToCsv reject input where two different paths would
produce the same key (for example {"a.b":1,"a":{"b":2}}), and
renameKeys rejects a mapping that makes two keys collide, instead of silently
overwriting one value.
Validate JSON data against a JSON Schema (Draft 7). Returns a structured error report with path, keyword, and message for every violation. Charged 2 credits on success only.
curl -X POST https://json.flyingfishspace.com/api/json/v1/schema \
-H "X-API-Key: ffs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"schema": {"type":"object","required":["name"]}, "data": {"age":30}}'
Response (200 — validation ran, data is invalid)
{
"success": true,
"data": {
"valid": false,
"errors": [
{ "path": "#", "keyword": "required",
"message": "Instance does not have required property \"name\"." }
]
},
"remaining_balance": 95
}
Response (422 — numeric precision would be lost, not charged)
{
"success": false,
"error": "Input contains numeric literal(s) exceeding safe precision (max 15 significant digits): \"1234567890.123456\" (16 significant digits). Encode high-precision numbers as strings instead (e.g. \"amount\": \"1234567890.123456789012\") to preserve exact values."
}
Response (422 — large-integer precision would be lost, not charged)
{
"success": false,
"error": "Input contains integer literal(s) exceeding safe precision (max 16 digits, Number.MAX_SAFE_INTEGER = 9007199254740991): \"22233344455566677788\" (20 digits). Encode large integers as strings instead (e.g. \"id\": \"22233344455566677788\") to preserve exact values."
}
200 response with valid: false is a successful validation run
that correctly found the data non-conformant — you are charged for performing the check,
not for the data happening to pass. If your schema uses uniqueItems,
arrays in the data are limited to 1000 items (a provisional limit); larger arrays are
rejected with 422 and not charged.
Repairs severely malformed JSON that rule-based cleaning cannot recover, and
optionally maps semantically non-conforming values (e.g. enum mismatches)
against a target schema. Powered by @cf/openai/gpt-oss-20b.
Unlike every other endpoint on this page, this one is not private-by-design —
your data is sent to a hosted AI model for processing.
/clean). If that alone succeeds, you are charged
1 credit — identical to calling /clean directly. Only when the
rule-based pass is insufficient and the AI model is actually invoked (for structural repair,
or semantic remapping against a supplied schema) are you charged the full
10 credits. Every repair is bounded by two guardrails regardless of tier:
the AI's output must re-validate against your schema (if supplied), and any field not declared
as an enum in that schema is value-locked — the AI is not permitted to silently
change it, or to fabricate a value for a required field that was genuinely missing from your input.
curl -X POST https://json.flyingfishspace.com/api/json/v1/ai-clean \
-H "X-API-Key: ffs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"rawInput": "{status: '"'"'ok'"'"', user_id: 42,}",
"schema": {
"type": "object",
"required": ["status", "user_id"],
"properties": {
"status": { "type": "string", "enum": ["active","inactive","pending"] },
"user_id": { "type": "integer" }
}
}
}'
Body parameters
Response (200 — repaired via rule engine only, 1 credit)
{
"success": true,
"data": { "status": "active", "user_id": 42 },
"changes": [],
"schema_valid": true,
"used_ai": false,
"remaining_balance": 99
}
Response (200 — AI invoked for semantic remapping, 10 credits)
{
"success": true,
"data": { "status": "active", "user_id": 42 },
"changes": [
{ "path": "status", "before": "ok", "after": "active" }
],
"schema_valid": true,
"used_ai": true,
"usage": { "total_tokens": 255, "neurons": 5.05 },
"remaining_balance": 90
}
Response (422 — AI fabricated a value for a missing required field, 1 credit charged)
{
"success": false,
"error": "AI repair rejected: changed protected value(s) at accountType",
"usage": { "total_tokens": 201, "neurons": 4.1 },
"ai_charged_credits": 1
}
Response (422 — AI output rejected by value-lock, 1 credit charged)
{
"success": false,
"error": "AI repair rejected: changed protected value(s) at status",
"usage": { "total_tokens": 189, "neurons": 3.72 },
"ai_charged_credits": 1
}
Response (422 — AI returned an empty or non-JSON output, 1 credit charged)
{
"success": false,
"error": "AI returned an empty or malformed response",
"usage": { "total_tokens": 4300, "neurons": 60.2 },
"ai_charged_credits": 1
}
Billing summary
ai-clean step inside a workflow.
required array is genuinely absent from
rawInput, the model is instructed to leave it absent rather than invent a
placeholder value — and if it does anyway, this guardrail rejects the repair. A missing
field should surface as missing, not as a plausible-looking fabricated value.
Response (413 — schema too large for AI repair, not charged)
{
"success": false,
"error": "Schema exceeds the AI repair size limit (8192 bytes; received 12034 bytes). Simplify the schema or split the work; no AI call was made and no credits were charged."
}
Response (413 — rawInput too large for AI repair, not charged)
{
"success": false,
"error": "Input exceeds the AI repair size limit (3072 bytes; received 5880 bytes). Please split large inputs into smaller batches and repair them separately. This limit is specific to /ai-clean — the rule-based /clean endpoint supports much larger payloads."
}
rawInput is capped at 3072 bytes (UTF-8) and schema
at a provisional 8192 bytes for this endpoint —
much smaller than the platform-wide 1MB limit. This is deliberate: repair quality and
output completeness were calibrated against real test batches, and inputs beyond this size
showed a measurable risk of the model silently truncating or altering content it shouldn't
have touched. Rather than risk an incomplete or subtly incorrect repair, oversized input is
rejected outright, before any AI call is made — so it's never charged. If you need to repair
a larger document, split it into smaller batches (e.g. per-record for arrays) and call this
endpoint separately for each.
Runs a predefined sequence of tools against your input in a single request, instead of
calling each endpoint separately. Each successfully-completed step is billed individually —
the total charge is the sum of each step's own cost, identical to calling those endpoints
one at a time. A step that fails, or a schema-validation step that reports the data as
invalid, halts the chain immediately: no further steps run, and steps after the halt point
are never charged.
The ai-clean step follows the same tiered billing as the standalone
endpoint — 1 credit if rule-based repair alone succeeds, 10 credits if the AI model
is invoked — so a workflow containing it may cost less than its listed maximum.
Available workflows
The full, current list — including maintenance status — is always available at
GET /api/v1/modules
(workflow entries are marked with "type": "workflow").
Body parameters
curl -X POST https://json.flyingfishspace.com/api/json/v1/workflow/clean-validate \
-H "X-API-Key: ffs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "{name: \"Alice\", age: 30,}",
"schema": {"type": "object", "required": ["name", "age"]}
}'
Response (200 — every step completed)
{
"success": true,
"combo": "clean-validate",
"steps": [
{ "segment": "clean", "success": true, "repairs": ["Removed trailing comma before '}'"] },
{ "segment": "schema", "success": true, "valid": true, "errors": [] }
],
"data": { "name": "Alice", "age": 30 },
"remaining_balance": 746
}
Response (422 — halted partway through, e.g. schema invalid)
{
"success": false,
"combo": "full-pipeline",
"steps": [
{ "segment": "clean", "success": true, "repairs": [] },
{ "segment": "schema", "success": true, "valid": false, "errors": [
{ "path": "#", "keyword": "required", "message": "Instance does not have required property \"name\"." }
] }
],
"data": { "age": 30 },
"abort_reason": "Schema validation failed; workflow halted before remaining steps.",
"charged_credits": 1,
"remaining_balance": 748
}
charged_credits reflects what this specific call actually billed — usually 0
for a halted workflow, but can be non-zero if a step (most commonly ai-clean)
consumed real AI compute before being rejected by a guardrail. See the AI-Assisted JSON
Repair section above for when that applies (a guardrail rejection, or an empty or non-JSON
model output: 1 credit; anything rejected before the AI is called: 0).
clean
was billed but transform (which never ran) was not. data always reflects
the document as of the last step that transformed it; a schema step never changes
data, since its own output is a validation report, not a document.
const res = await fetch(
'https://json.flyingfishspace.com/api/json/v1/clean',
{
method: 'POST',
headers: {
'X-API-Key': 'ffs_live_YOUR_KEY',
'Content-Type': 'application/json',
},
body: brokenJsonString,
}
);
const { success, data, repairs, remaining_balance } = await res.json();
if (success) console.log(data);
import requests
res = requests.post(
"https://json.flyingfishspace.com/api/json/v1/clean",
headers={"X-API-Key": "ffs_live_YOUR_KEY"},
data=broken_json_string,
)
result = res.json()
if result["success"]:
print(result["data"])
req, _ := http.NewRequest("POST",
"https://json.flyingfishspace.com/api/json/v1/clean",
strings.NewReader(brokenJSON))
req.Header.Set("X-API-Key", "ffs_live_YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
429 (never
charged). Per-API-Key limits are the primary limits for authenticated
traffic and apply once your key has been verified. Per-IP limits are a
coarser, unauthenticated-traffic guard that sits well above normal per-Key usage and
should not be reached by legitimate authenticated calls.
Per-API-Key limits (authenticated traffic)
Per-IP limits — requests carrying an X-API-Key (edge-layer abuse guard)
Per-IP limits — requests with no X-API-Key at all (stricter; likely a probe, not a legitimate caller)
X-API-Key to return any result — a request
without one can never succeed here, so keyless traffic at volume is treated as a probe, not
a legitimate caller still shopping for a key, and gets a tighter limit and a longer cooldown.
Payload size