NoRekey API API

Four endpoints: upload a statement, poll its status, download the result in any supported format, and check the team's page balance. Authentication is a bearer API key (created under Settings → Team → API keys; keys are shown once and start with `nrk_`). API conversions draw from the same monthly pa

Operations 4

POST /api/v1/conversions Upload a statement for conversion #
GET /api/v1/conversions/{conversion} Poll a conversion #
GET /api/v1/conversions/{conversion}/export/{format} Download a completed conversion #
GET /api/v1/usage Check the team's page balance #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/norekey:norekey-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

norekey-api-openapi.json Raw ↑
{
    "openapi": "3.1.0",
    "info": {
        "title": "NoRekey API",
        "version": "1.0.0",
        "summary": "Convert PDF bank statements to CSV, Excel, OFX, QFX and JSON — every conversion verified against the statement's own balances.",
        "description": "Four endpoints: upload a statement, poll its status, download the result in any supported format, and check the team's page balance. Authentication is a bearer API key (created under Settings → Team → API keys; keys are shown once and start with `nrk_`). API conversions draw from the same monthly page allowance as the web converter, on every plan including Free. Rate limit: 120 requests per minute per API key. Source PDFs are deleted the moment processing finishes; statement passwords are used once to open the file and never stored.",
        "termsOfService": "https://norekey.com/terms",
        "contact": {
            "name": "NoRekey support",
            "email": "support@norekey.com",
            "url": "https://norekey.com/help/using-the-api"
        }
    },
    "servers": [{ "url": "https://norekey.com" }],
    "security": [{ "apiKey": [] }],
    "paths": {
        "/api/v1/conversions": {
            "post": {
                "operationId": "createConversion",
                "summary": "Upload a statement for conversion",
                "description": "Accepts the statement either as a multipart file upload or as base64 inside a JSON body (for callers where multipart is awkward). PDFs only, up to 20 MB. Password-protected PDFs convert in one step when the password is supplied; scanned statements convert through OCR automatically and are charged like any other page (`pages_charged` reports the cost). Returns the conversion document immediately — poll it until `status` is `completed`.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "required": ["statement"],
                                "properties": {
                                    "statement": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "The statement PDF, up to 20 MB."
                                    },
                                    "password": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "Password for a protected PDF. Used once to open the file, never stored."
                                    }
                                }
                            }
                        },
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": ["statement", "filename"],
                                "properties": {
                                    "statement": {
                                        "type": "string",
                                        "contentEncoding": "base64",
                                        "description": "The statement PDF, base64-encoded. Decoded size limit is the same 20 MB."
                                    },
                                    "filename": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "Original filename, used for display and exports."
                                    },
                                    "password": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "Password for a protected PDF. Used once to open the file, never stored."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Statement accepted; conversion queued or processing.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Conversion"
                                }
                            }
                        }
                    },
                    "401": { "$ref": "#/components/responses/Unauthenticated" },
                    "422": {
                        "description": "The statement was rejected. `code` identifies why — `password_required` (protected PDF, no password given), `quota_exceeded` (the statement would take the team over its monthly page allowance), `too_many_pages` (over the per-file page limit), or an `invalid_statement`/unreadable-PDF code with a human-readable `message`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": { "$ref": "#/components/responses/RateLimited" }
                }
            }
        },
        "/api/v1/conversions/{conversion}": {
            "get": {
                "operationId": "getConversion",
                "summary": "Poll a conversion",
                "description": "Returns the conversion document. Poll until `status` is `completed` (or `failed`); `expected_seconds` is a live estimate of total processing time for a statement of this size. Completed conversions report the detected `bank` and `currency`, whether the extraction `balanced` against the statement's own opening and closing figures, and the formats available for export.",
                "parameters": [
                    { "$ref": "#/components/parameters/conversionId" }
                ],
                "responses": {
                    "200": {
                        "description": "The conversion document.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Conversion"
                                }
                            }
                        }
                    },
                    "401": { "$ref": "#/components/responses/Unauthenticated" },
                    "404": { "$ref": "#/components/responses/NotFound" },
                    "429": { "$ref": "#/components/responses/RateLimited" }
                }
            }
        },
        "/api/v1/conversions/{conversion}/export/{format}": {
            "get": {
                "operationId": "downloadExport",
                "summary": "Download a completed conversion",
                "description": "Streams the converted statement in the requested format as a file download. Available once `status` is `completed`, until the conversion expires on the team's retention schedule (expired conversions return 410).",
                "parameters": [
                    { "$ref": "#/components/parameters/conversionId" },
                    {
                        "name": "format",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "enum": ["csv", "xlsx", "ofx", "qfx", "json"]
                        },
                        "description": "The export format."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The converted statement as a file download.",
                        "content": {
                            "text/csv": { "schema": { "type": "string" } },
                            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/x-ofx": {
                                "schema": { "type": "string" }
                            },
                            "application/json": {
                                "schema": { "type": "object" }
                            }
                        }
                    },
                    "401": { "$ref": "#/components/responses/Unauthenticated" },
                    "404": {
                        "description": "Unknown conversion, a conversion belonging to another team, or an unknown export format."
                    },
                    "410": {
                        "description": "The conversion has expired on the team's retention schedule and its data has been deleted."
                    },
                    "429": { "$ref": "#/components/responses/RateLimited" }
                }
            }
        },
        "/api/v1/usage": {
            "get": {
                "operationId": "getUsage",
                "summary": "Check the team's page balance",
                "description": "Returns the page balance of the team the API key belongs to: the plan's allowance for the current usage cycle, the pages charged against it so far, the credit balance on top, and `available`, the total an upload is checked against. A statement uploads when its `pages_charged` (its page count, scanned or not) is no more than `available`; otherwise the upload is refused with `quota_exceeded`. Pages are charged when a conversion is accepted and refunded if it fails. Allowance figures are `null` on unlimited plans. The cycle resets monthly at `cycle.resets_at`; credit pages do not reset. Calling this endpoint costs no pages.",
                "responses": {
                    "200": {
                        "description": "The team's page balance.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Usage"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "apiKey": {
                "type": "http",
                "scheme": "bearer",
                "description": "Team API key, sent as `Authorization: Bearer nrk_…`. Create keys under Settings → Team → API keys."
            }
        },
        "parameters": {
            "conversionId": {
                "name": "conversion",
                "in": "path",
                "required": true,
                "schema": { "type": "string" },
                "description": "The conversion id (ULID) returned when the statement was uploaded."
            }
        },
        "responses": {
            "Unauthenticated": {
                "description": "Missing, malformed or revoked API key."
            },
            "NotFound": {
                "description": "No such conversion visible to this API key — keys see exactly their own team's conversions."
            },
            "RateLimited": {
                "description": "Rate limit exceeded — 120 requests per minute per API key."
            }
        },
        "schemas": {
            "Conversion": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "ULID identifying the conversion."
                    },
                    "statement": {
                        "type": "string",
                        "description": "Original filename of the uploaded PDF."
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "processing",
                            "completed",
                            "failed",
                            "expired"
                        ]
                    },
                    "pages": {
                        "type": ["integer", "null"],
                        "description": "Page count of the statement."
                    },
                    "scanned": {
                        "type": "boolean",
                        "description": "Whether the statement was detected as scanned and converted through OCR."
                    },
                    "pages_charged": {
                        "type": ["integer", "null"],
                        "description": "Pages drawn from the monthly allowance — the page count, scanned or not."
                    },
                    "expected_seconds": {
                        "type": "integer",
                        "description": "Live estimate of total processing time for a statement of this size, in seconds."
                    },
                    "bank": {
                        "type": ["string", "null"],
                        "description": "Bank or issuer detected from the statement, once completed."
                    },
                    "currency": {
                        "type": ["string", "null"],
                        "description": "Currency detected from the statement, once completed."
                    },
                    "balanced": {
                        "type": ["boolean", "null"],
                        "description": "Whether the extracted transactions reconciled against the statement's own opening and closing balances. False means the conversion is flagged for review — inspect before trusting."
                    },
                    "opening_balance": {
                        "type": ["number", "string", "null"],
                        "description": "The statement's declared opening balance."
                    },
                    "closing_balance": {
                        "type": ["number", "string", "null"],
                        "description": "The statement's declared closing balance."
                    },
                    "error": {
                        "type": ["string", "null"],
                        "description": "Human-readable failure reason when status is failed."
                    },
                    "formats": {
                        "type": "array",
                        "items": { "type": "string" },
                        "description": "Export formats available from the export endpoint."
                    },
                    "created_at": {
                        "type": ["string", "null"],
                        "format": "date-time"
                    },
                    "completed_at": {
                        "type": ["string", "null"],
                        "format": "date-time"
                    }
                }
            },
            "Usage": {
                "type": "object",
                "required": ["team", "plan", "cycle", "pages", "limits"],
                "properties": {
                    "team": {
                        "type": "object",
                        "required": ["name", "slug"],
                        "description": "The team the API key belongs to.",
                        "properties": {
                            "name": {
                                "type": "string"
                            },
                            "slug": {
                                "type": "string"
                            }
                        }
                    },
                    "plan": {
                        "type": "string",
                        "description": "The team's plan, e.g. `free`, `practice` or `firm`."
                    },
                    "cycle": {
                        "type": "object",
                        "required": ["started_at", "resets_at"],
                        "description": "The current monthly usage cycle. Paid teams' cycles follow their billing date.",
                        "properties": {
                            "started_at": {
                                "type": "string",
                                "format": "date-time"
                            },
                            "resets_at": {
                                "type": "string",
                                "format": "date-time",
                                "description": "When `used` returns to zero."
                            }
                        }
                    },
                    "pages": {
                        "type": "object",
                        "required": [
                            "allowance",
                            "used",
                            "remaining",
                            "credit",
                            "available"
                        ],
                        "properties": {
                            "allowance": {
                                "type": ["integer", "null"],
                                "minimum": 0,
                                "description": "Pages the plan includes per cycle; null when unlimited."
                            },
                            "used": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Pages charged this cycle: accepted, in-progress and completed conversions. Failed conversions are refunded."
                            },
                            "remaining": {
                                "type": ["integer", "null"],
                                "minimum": 0,
                                "description": "What's left of the plan allowance this cycle (never negative); null when unlimited."
                            },
                            "credit": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Extra pages from page packs the team has bought or pages we've added. Spent only once the plan allowance runs out; they never expire."
                            },
                            "available": {
                                "type": ["integer", "null"],
                                "minimum": 0,
                                "description": "`remaining` plus `credit`: the most pages the next upload may be charged. Null when unlimited."
                            }
                        }
                    },
                    "limits": {
                        "type": "object",
                        "required": [
                            "max_pages_per_file",
                            "max_file_mb",
                            "scanned_page_multiplier"
                        ],
                        "properties": {
                            "max_pages_per_file": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Largest statement accepted, in pages."
                            },
                            "max_file_mb": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Largest file accepted, in megabytes."
                            },
                            "scanned_page_multiplier": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Always 1 \u2014 scanned pages are charged like any other. Kept so existing clients don't break."
                            }
                        }
                    }
                }
            },
            "Error": {
                "type": "object",
                "required": ["code", "message"],
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "Machine-readable error identifier, e.g. password_required, quota_exceeded, too_many_pages, invalid_statement."
                    },
                    "message": {
                        "type": "string",
                        "description": "Human-readable explanation."
                    }
                }
            }
        }
    }
}