dataforge — structured data, reshaped deterministically

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.

Paying. Every 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.

Endpoints & prices

RoutePriceWhat it does
POST /convert$0.002Convert between any pair of the supported formats. RFC 4180 CSV, format sniffing with from:"auto", precision-safe type coercion.
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".
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.
POST /transform$0.003A declarative pipeline over tabular or nested data. No user code is ever evaluated — every step is a named primitive.
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.
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.
POST /detectfreeSniff the format of a document and report the confidence and the reason.
GET /openapi.jsonfreeOpenAPI 3.1 description of every route.
GET /healthfree{"ok":true}

POST /convert $0.002

Convert between any pair of the supported formats. RFC 4180 CSV, format sniffing with from:"auto", precision-safe type coercion.

Request

{
  "from": "csv",
  "to": "json",
  "data": "name,age\r\nAda,36\r\n\"Grace, R\",45\n"
}

Response

{
  "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

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.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".

Request

{
  "from": "json",
  "data": "{\"store\":{\"book\":[{\"author\":\"Rees\",\"price\":8.95},{\"author\":\"Waugh\",\"price\":12.99}]}}",
  "path": "$.store.book[?(@.price < 10)].author"
}

Response

{
  "count": 1,
  "matches": ["Rees"],
  "paths": ["$.store.book[0].author"],
  "pointers": ["/store/book/0/author"]
}

curl

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.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.

Request

{
  "from": "json",
  "data": "[{\"id\":1,\"email\":\"a@b.com\",\"tier\":\"pro\"},{\"id\":2,\"email\":\"c@d.io\",\"tier\":\"free\"}]"
}

Response

{
  "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

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.003

A declarative pipeline over tabular or nested data. No user code is ever evaluated — every step is a named primitive.

Request

{
  "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"] }
  ]
}

Response

{
  "format": "csv",
  "rows": 2,
  "columns": ["dept", "headcount", "total"],
  "data": "dept,headcount,total\neng,2,230\nops,1,100\n"
}

curl

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.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.

Request

{
  "from": "yaml",
  "a": "name: app\nreplicas: 2\n",
  "b": "name: app\nreplicas: 3\nimage: nginx\n"
}

Response

{
  "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

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.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.

Request

{
  "from": "yaml",
  "to": "yaml",
  "data": "name: app\nreplicas: 2\n",
  "patch": [{ "op": "replace", "path": "/replicas", "value": 3 }]
}

Response

{
  "format": "yaml",
  "applied": 1,
  "data": "name: app\nreplicas: 3\n"
}

curl

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 }] }'

Transform operations

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.

Honest limits

Errors

{ "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.