Convert, query, describe, reshape and diff structured data across nine formats. Hand-written parsers, no guessing, no LLM in the loop. Pay per call over x402 — USDC on Base, no account, no API key.
POST route is metered with x402 v2. An unpaid
request returns 402 with a PAYMENT-REQUIRED challenge; any x402
client (@x402/fetch, x402-axios, an MCP wallet tool) pays and
retries automatically. Free and unmetered: GET /,
GET /health, GET /openapi.json, GET /formats,
POST /detect. Requests that fail validation are never charged.
| Route | Price | What it does |
|---|---|---|
POST /convert | $0.002 | Convert between any pair of the supported formats. RFC 4180 CSV, format sniffing with from:"auto", precision-safe type coercion. |
POST /query | $0.002 | JSONPath over a document in any format: recursive descent, wildcards, unions, slices with step, and filter expressions. Also RFC 6901 JSON Pointer with syntax:"pointer". |
POST /schema | $0.003 | Infer a JSON Schema (draft 2020-12) from samples — merged across array elements, with required/optional, enums, formats, integer vs number and nullability. Pass validate instead to validate and get per-path errors. |
POST /transform | $0.003 | A declarative pipeline over tabular or nested data. No user code is ever evaluated — every step is a named primitive. |
POST /diff | $0.002 | Structural diff of two documents in any format → an RFC 6902 JSON Patch plus a readable summary. Arrays are diffed with an LCS, so inserts stay minimal. |
POST /patch | $0.002 | Apply an RFC 6902 patch (add / remove / replace / move / copy / test) and get the result back in any format. /diff then /patch reproduces the target byte-for-byte. |
POST /detect | free | Sniff the format of a document and report the confidence and the reason. |
GET /openapi.json | free | OpenAPI 3.1 description of every route. |
GET /health | free | {"ok":true} |
POST /convert $0.002Convert between any pair of the supported formats. RFC 4180 CSV, format sniffing with from:"auto", precision-safe type coercion.
{
"from": "csv",
"to": "json",
"data": "name,age\r\nAda,36\r\n\"Grace, R\",45\n"
}
{
"format": "json",
"from": "csv",
"data": "[\n {\n \"name\": \"Ada\",\n \"age\": 36\n },\n {\n \"name\": \"Grace, R\",\n \"age\": 45\n }\n]\n",
"rows": 2,
"warnings": []
}
curl -sS https://dataforge.x.c00l.site/convert \
-H 'content-type: application/json' \
-d '{ "from": "csv", "to": "json", "data": "name,age\r\nAda,36\r\n\"Grace, R\",45\n" }'
POST /query $0.002JSONPath over a document in any format: recursive descent, wildcards, unions, slices with step, and filter expressions. Also RFC 6901 JSON Pointer with syntax:"pointer".
{
"from": "json",
"data": "{\"store\":{\"book\":[{\"author\":\"Rees\",\"price\":8.95},{\"author\":\"Waugh\",\"price\":12.99}]}}",
"path": "$.store.book[?(@.price < 10)].author"
}
{
"count": 1,
"matches": ["Rees"],
"paths": ["$.store.book[0].author"],
"pointers": ["/store/book/0/author"]
}
curl -sS https://dataforge.x.c00l.site/query \
-H 'content-type: application/json' \
-d '{ "from": "json", "data": "{\"store\":{\"book\":[{\"author\":\"Rees\",\"price\":8.95},{\"author\":\"Waugh\",\"price\":12.99}]}}", "path": "$.store.book[?(@.price < 10)].author" }'
POST /schema $0.003Infer a JSON Schema (draft 2020-12) from samples — merged across array elements, with required/optional, enums, formats, integer vs number and nullability. Pass validate instead to validate and get per-path errors.
{
"from": "json",
"data": "[{\"id\":1,\"email\":\"a@b.com\",\"tier\":\"pro\"},{\"id\":2,\"email\":\"c@d.io\",\"tier\":\"free\"}]"
}
{
"mode": "infer",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"email": { "type": "string", "format": "email" },
"tier": { "enum": ["pro", "free"] }
},
"required": ["id", "email", "tier"]
}
}
}
curl -sS https://dataforge.x.c00l.site/schema \
-H 'content-type: application/json' \
-d '{ "from": "json", "data": "[{\"id\":1,\"email\":\"a@b.com\",\"tier\":\"pro\"},{\"id\":2,\"email\":\"c@d.io\",\"tier\":\"free\"}]" }'
POST /transform $0.003A declarative pipeline over tabular or nested data. No user code is ever evaluated — every step is a named primitive.
{
"from": "csv",
"to": "csv",
"data": "dept,name,salary\neng,Ada,120\neng,Alan,110\nops,Grace,100\n",
"pipeline": [
{ "op": "groupBy", "by": ["dept"],
"aggregations": { "headcount": "count", "total": "sum:salary" } },
{ "op": "sort", "by": ["-total"] }
]
}
{
"format": "csv",
"rows": 2,
"columns": ["dept", "headcount", "total"],
"data": "dept,headcount,total\neng,2,230\nops,1,100\n"
}
curl -sS https://dataforge.x.c00l.site/transform \
-H 'content-type: application/json' \
-d '{ "from": "csv", "to": "csv", "data": "dept,name,salary\neng,Ada,120\neng,Alan,110\nops,Grace,100\n", "pipeline": [ { "op": "groupBy", "by": ["dept"], "aggregations": { "headcount": "count", "total": "sum:salary" } }, { "op": "sort", "by": ["-total"] } ] }'
POST /diff $0.002Structural diff of two documents in any format → an RFC 6902 JSON Patch plus a readable summary. Arrays are diffed with an LCS, so inserts stay minimal.
{
"from": "yaml",
"a": "name: app\nreplicas: 2\n",
"b": "name: app\nreplicas: 3\nimage: nginx\n"
}
{
"equal": false,
"patch": [
{ "op": "replace", "path": "/replicas", "value": 3 },
{ "op": "add", "path": "/image", "value": "nginx" }
],
"summary": ["changed /replicas: 2 -> 3", "added /image = \"nginx\""],
"counts": { "replace": 1, "add": 1, "total": 2 }
}
curl -sS https://dataforge.x.c00l.site/diff \
-H 'content-type: application/json' \
-d '{ "from": "yaml", "a": "name: app\nreplicas: 2\n", "b": "name: app\nreplicas: 3\nimage: nginx\n" }'
POST /patch $0.002Apply an RFC 6902 patch (add / remove / replace / move / copy / test) and get the result back in any format. /diff then /patch reproduces the target byte-for-byte.
{
"from": "yaml",
"to": "yaml",
"data": "name: app\nreplicas: 2\n",
"patch": [{ "op": "replace", "path": "/replicas", "value": 3 }]
}
{
"format": "yaml",
"applied": 1,
"data": "name: app\nreplicas: 3\n"
}
curl -sS https://dataforge.x.c00l.site/patch \
-H 'content-type: application/json' \
-d '{ "from": "yaml", "to": "yaml", "data": "name: app\nreplicas: 2\n", "patch": [{ "op": "replace", "path": "/replicas", "value": 3 }] }'
Steps run in order. Every operation is a fixed primitive with validated arguments; expressions
are parsed and interpreted, never eval'd.
filter takes either a predicate object
({"field":"price","op":"lt","value":10}, composable with
and/or/not) or an expression string
("price < 10 && tier == 'pro'"). groupBy aggregations:
count, countDistinct, sum, avg,
min, max, first, last,
join, list.
|/> with
chomping and explicit indentation, anchors, aliases, << merge keys,
multiple documents and the YAML 1.2 core schema. Not supported, and
rejected rather than mis-parsed: explicit keys (? foo), non-scalar keys,
%TAG directives and custom tags, !!set/!!omap/
!!binary. Tabs used for indentation are an error.007 stays
"007"; a 19-digit id stays a string rather than rounding. Quoted CSV
fields are never coerced. Set options.coerce to
"aggressive" for classic loose parsing, or "none" for
strings only.null: null keys are dropped on
output (configurable).<!ENTITY>
declarations return 400 — that is the billion-laughs defence — and
external SYSTEM/PUBLIC identifiers are never fetched.
Only the five predefined entities and numeric character references are expanded,
with a total expansion cap.syntax:"jmespath" gets a clear 400.data 1,048,576 chars,
body 2 MiB,
depth 64,
200,000 rows,
20,000 query matches.
Over the cap is a 400/413, not a crash.{ "error": { "code": "csv_parse_error",
"message": "CSV parse error at line 3, column 12: unterminated quoted field",
"details": { "line": 3, "column": 12 } } }
A 4xx or 5xx is never settled, so a failed call is free.