← Back to Nominal
Public API Reference

Nominal Public API — v1

Base URL: https://nominal-vrkp.polsia.app Updated July 26, 2026

The Nominal Public API exposes the same JSON↔CSV conversion the web app runs, callable from curl, JavaScript, Python, or any HTTP client. Authentication is via API keys minted from your account's /settings page. The endpoint accepts up to 1 MB per request and returns the converted output as JSON.

Quickstart

Three steps to your first successful call:

  1. Subscribe to Pro from /pricing — the Public API is a Pro-only feature.
  2. Mint an API key from /settings by clicking Generate new key. Copy the raw token — the dashboard only shows the first 16 characters.
  3. Call POST /api/v1/convert with the raw key as a Bearer token.
curl

One-line call from your terminal. Replace <YOUR_API_KEY>.

bash — POST /api/v1/convert
# Convert a JSON array to CSV.
curl -sX POST https://nominal-vrkp.polsia.app/api/v1/convert \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"input":"[{\"a\":1},{\"a\":2}]","direction":"json_to_csv"}'
200 — expected response
{
  "success": true,
  "direction": "json_to_csv",
  "output": "a\n1\n2\n",
  "bytes_in": 21,
  "bytes_out": 6
}
JavaScript

Browser- or Node-compatible fetch. Replace <YOUR_API_KEY>.

javascript — fetch
const res = await fetch(
  'https://nominal-vrkp.polsia.app/api/v1/convert',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer <YOUR_API_KEY>',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      input: '[{"a":1},{"a":2}]',
      direction: 'json_to_csv'
    })
  }
);
const json = await res.json();
200 — expected response
{
  "success": true,
  "direction": "json_to_csv",
  "output": "a\n1\n2\n",
  "bytes_in": 21,
  "bytes_out": 6
}
Python

Single requests.post. Replace <YOUR_API_KEY>.

python — requests
import requests

r = requests.post(
    "https://nominal-vrkp.polsia.app/api/v1/convert",
    headers={
        "Authorization": "Bearer <YOUR_API_KEY>",
        "Content-Type": "application/json",
    },
    json={"input": '[{"a":1},{"a":2}]', "direction": "json_to_csv"},
    timeout=30,
)
r.raise_for_status()
print(r.json()["output"])
200 — expected response
{
  "success": true,
  "direction": "json_to_csv",
  "output": "a\n1\n2\n",
  "bytes_in": 21,
  "bytes_out": 6
}

Authentication

Every request must include an Authorization header of the form:

http
Authorization: Bearer nk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are sha256-hashed server-side; only the prefix and the hash live in our database. The raw key is shown exactly once at mint time — store it somewhere safe immediately. If you lose it, revoke the old key and mint a new one from /settings.

POST /api/v1/convert

Convert a JSON array to CSV, or CSV back to a JSON array. The endpoint uses dot-notation flattening for nested objects and arrays are serialized as JSON strings within a single cell.

Request body schema
FieldTypeRequiredNotes
inputstringyesSource payload — JSON array (for json_to_csv/json_to_yaml), CSV text (for csv_to_json/csv_to_yaml), or YAML text (for yaml_to_json/yaml_to_csv). Max 1 MB.
directionstringyesOne of json_to_csv, csv_to_json, json_to_yaml, yaml_to_json, csv_to_yaml, or yaml_to_csv.
Response schema

On success, the response is JSON with the converted output as a string field. The byte counts let you size requests:

FieldTypeNotes
successbooleanAlways true on a 200 response.
directionstringEchoes the direction you sent.
outputstringConverted payload, encoded as a JSON string.
bytes_inintegerSize of input in UTF-8 bytes.
bytes_outintegerSize of output in UTF-8 bytes.
Status codes
CodeMeaningWhen
200OKConversion succeeded. Body contains the converted output.
400Bad requestMissing input/direction, invalid direction, or unparseable input. Body has { "error": "..." }.
401UnauthenticatedNo Authorization header or the key is invalid. Body: { "error": "auth_required" | "invalid_key" }.
403ForbiddenKey is valid but the account is not on Pro. Body: { "error": "pro_required" }.
413Payload too largeThe input field exceeds 1 MB. Body: { "error": "payload_too_large", "max_bytes": 1048576 }.

Code samples

Each tab is a real, copy-pasteable example. Replace <YOUR_API_KEY> with the raw key from /settings; replace the example payload with your own data.

bash
# Convert a JSON array to CSV.
curl -sX POST https://nominal-vrkp.polsia.app/api/v1/convert \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"input":"[{\"a\":1},{\"a\":2}]","direction":"json_to_csv"}'

# Convert a CSV back to JSON.
curl -sX POST https://nominal-vrkp.polsia.app/api/v1/convert \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d "{\"input\":\"a,b\\n1,2\\n3,4\",\"direction\":\"csv_to_json\"}"
javascript — node or browser fetch
const API_KEY = '<YOUR_API_KEY>';
const BASE = 'https://nominal-vrkp.polsia.app';

async function convert(input, direction) {
  const res = await fetch(`${BASE}/api/v1/convert`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: '{"input":"[{\\"a\\":1},{\\"a\\":2}]","direction":"json_to_csv"}'
  });
  const json = await res.json();
  if (!res.ok) throw new Error(json.error || 'convert failed');
  return json.output;
}

convert('[{"a":1},{"a":2}]', 'json_to_csv')
  .then(csv => console.log(csv));
  // 'a\n1\n2\n'
python — requests
import json
import requests

API_KEY = "<YOUR_API_KEY>"
BASE = "https://nominal-vrkp.polsia.app"

def convert(text: str, direction: str) -> str:
    r = requests.post(
        f"{BASE}/api/v1/convert",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={"input": text, "direction": direction},
        timeout=30,
    )
    r.raise_for_status()
    return r.json()["output"]

print(convert('[{"a":1},{"a":2}]', "json_to_csv"))
# 'a\n1\n2\n'

Rate limits

Pro subscribers have no daily cap on API usage. The 2/day limit enforced for free web usage does not apply to the Public API — your API key must belong to an active Pro subscription, and once it does, conversion counts share the same UTC-day window only for diagnostics.

The response headers advertise the current quota state so you can surface it in your own monitoring:

http — response headers
X-Conversions-Used: 0
X-Conversions-Limit: unlimited

For free accounts, when an API key is somehow minted before the subscription is active, the endpoint returns 403 { "error": "pro_required" } — never a partial quota. There is no per-minute rate limit enforced in v1; abusive traffic should be raised with support@nominal.app.

Errors

All errors are returned with a JSON body of { "error": "<code>", ... }. Stable error codes so your retry logic can branch on them.

Error code reference
CodeHTTPFix
auth_required401Add an Authorization: Bearer <YOUR_API_KEY> header. The endpoint does not accept query-param auth.
invalid_key401The key is unknown or revoked. Mint a new key from /settings.
pro_required403The account isn't on Pro. Subscribe at /pricing, then call again.
missing_field400You omitted input or direction. Both are required.
direction_invalid400direction must be json_to_csv or csv_to_json.
invalid_input400The input field could not be parsed as the requested source format. The error message includes the parser reason.
payload_too_large413input exceeded 1 MB. Split the payload and call again.
internal_error500Server-side failure. Safe to retry with exponential backoff.
Trying it from your terminal? After replacing <YOUR_API_KEY>, the curl example at the top of this page should round-trip [{"a":1},{"a":2}]a\n1\n2\n. If you see anything else, the Errors table above maps to remediation.
New to the API? Read the launch announcement for a "what is this and why should I care" overview, then come back here for the full reference.
Status: check uptime at /status before opening a support ticket.
← Looking for the free web app? Open the converter