# Bank Statement Converter: PDF to CSV, Excel, QuickBooks & Xero (`humble-echidna/bank-statement-converter`) Actor

Convert bank and credit card statement PDFs and bank CSV exports into transactions: date, description, amount, running balance and currency. Every statement gets a balance check (opening + transactions = closing). Download CSV, Excel or JSON, plus QuickBooks, Xero and OFX files.

- **URL**: https://apify.com/humble-echidna/bank-statement-converter.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Categories:** Automation, Developer tools, Integrations
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Bank Statement Converter do?

**Bank Statement Converter** turns **bank statement PDFs** and **bank CSV exports** into clean transactions: one row
per transaction with the **date, description, amount** (in cents and as a decimal), **running balance** and
**currency**. Every statement also gets a summary row with a **balance check**: opening balance + transactions =
closing balance, passed or failed, and by how much.

Download the rows as **CSV, Excel or JSON** from the Output tab, or have each statement written as a **QuickBooks
Online CSV**, a **Xero CSV** or an **OFX** file, ready to import. Statements that fail the balance check come back
free, with the rows where the running balance stops adding up.

[Input fields](https://apify.com/humble-echidna/bank-statement-converter/input-schema) ·
[API](https://apify.com/humble-echidna/bank-statement-converter/api) ·
[Use it from Claude, ChatGPT or Cursor (MCP)](#can-i-use-bank-statement-converter-from-an-ai-agent-mcp)

**Jump to:** [Fields](#what-data-does-bank-statement-converter-return) ·
[Price](#how-much-does-it-cost-to-convert-a-bank-statement) ·
[How to use](#how-to-convert-a-bank-statement-pdf-to-csv-or-excel) · [Input](#input) · [Output](#output) ·
[Balance check](#the-balance-check) · [What it reads](#what-it-reads-and-what-it-doesnt) ·
[AI agents (MCP)](#can-i-use-bank-statement-converter-from-an-ai-agent-mcp) · [Limits](#limits) · [FAQ](#faq)

**Try it in one click:** the input comes pre-filled with a sample statement (a 3-page checking account statement from
an invented bank, 73 transactions). That's 1 statement, or $0.05 (1 × $0.05, plus $0.00005 for the run start).
**Then replace it with links to your own statements.**

### What data does Bank Statement Converter return?

| Field | Example | Notes |
|---|---|---|
| `rowType` | `transaction` | `statement` for a statement's summary row, `transaction` for each transaction. |
| `date` | `2025-01-03` | Transaction date, `YYYY-MM-DD`. `postedDate` too when the statement prints two dates. |
| `description` | `ONLINE TRANSFER TO SAVINGS XXXXXX9884 REF #IBXWDE4PUH` | As printed, wrapped lines joined. |
| `amountCents`, `amount` | `-57225`, `-572.25` | Negative = money out (or a card charge), positive = money in (or a card payment). |
| `balanceCents`, `balance` | `147798`, `1477.98` | The running balance printed on the row; `null` when the row shows none. |
| `currency` | `USD` | Read from the statement, or the **Currency** you set. |
| `balanceCheck` | `verified` | Summary rows: `verified`, `mismatch` or `unavailable` (see [The balance check](#the-balance-check)). |
| `openingBalance`, `sumOfTransactions`, `closingBalance`, `difference` | `3606.09`, `3457.86`, `7063.95`, `0` | Summary rows; each also in cents (`openingBalanceCents`, ...). |
| `badRows` | `[]` | Summary rows: transactions where the running balance stops adding up. |
| `institution`, `accountLast4`, `periodStart`, `periodEnd` | `Copperfield Trust`, `1544`, `2025-01-01`, `2025-01-31` | As printed on a PDF statement; `null` when not printed. |
| `exportFiles` | `[{"format": "quickbooks", "key": "...", "url": "..."}]` | Summary rows: the accounting files written for the statement. |
| `statementId` | `sample-statement-cd346ae9` | Links each transaction to its statement; the same on every run for the same link. |

Each statement's summary row comes first, then its transactions. The full list of fields is under [Output](#output).

### How much does it cost to convert a bank statement?

You pay per statement converted: **$50.00 per 1,000 statements** ($0.05 each, whatever the number of pages or
transactions), plus $0.00005 each time a run starts. The rows, the summary and the QuickBooks, Xero and OFX files
are included.

- **The example below:** 1 statement = $0.05, plus the start fee.
- **A month, for example:** a bookkeeper converting 30 clients' checking and card statements, 60 statements:
  60 × $0.05 = **$3.00**.
- **Caps:** **Max statements per run** in the input, and **Maximum cost per run** in the run options. The run stops
  cleanly at whichever comes first: a statement is only delivered if it can be paid for, and links past the limit
  aren't downloaded (they're listed in `RUN_STATS` as `queriesSkipped`).

**Never charged:** a statement that **fails the balance check** (its rows still come back, free, so you can see
what's off), a CSV export with rows that couldn't be imported (each listed in `rowErrors`), a statement with no
transactions, and links that fail: scanned PDFs, password-protected PDFs, files over **Max pages per statement**, web
pages, missing files, or addresses the site's `robots.txt` disallows.

A PDF that holds several accounts (savings and checking on one credit-union statement) counts each account as one
statement.

### How to convert a bank statement PDF to CSV or Excel

1. Open Bank Statement Converter and click **Try for free** (or **Start** if you're signed in).
2. Put direct links to your statements in **Statement URLs**, one per line: PDFs as your bank sends them, or CSV
   exports from online banking.
3. Optional: pick **Also write accounting files** (QuickBooks, Xero, OFX), set **Date order** for day-first dates,
   **Currency** for CSV files without a currency symbol, or **Flip CSV signs** for card exports.
4. Click **Start**. The **Transactions** view of the Output tab shows the rows, the **Statements (balance check)** view
   the checks. Export as CSV, Excel or JSON; the accounting files are in the run's key-value store (**Storage** tab).

### Example: the sample statement

The pre-filled input, with the accounting files switched on:

```json
{"urls": ["https://whitehatharbor.com/statements/sample-statement.pdf"],
 "exportFormats": ["quickbooks", "xero", "ofx"]}
```

It returned 1 summary row and 73 transactions. The summary row (real output from a local run on 2026-10-13; on Apify
each file also gets its download `url`):

```json
{
  "rowType": "statement",
  "statementId": "sample-statement-cd346ae9",
  "fileName": "sample-statement.pdf",
  "pageCount": 3,
  "statementKind": "bank",
  "institution": "Copperfield Trust",
  "accountLast4": "1544",
  "periodStart": "2025-01-01",
  "periodEnd": "2025-01-31",
  "currency": "USD",
  "transactionCount": 73,
  "openingBalanceCents": 360609,
  "openingBalance": 3606.09,
  "sumOfTransactionsCents": 345786,
  "sumOfTransactions": 3457.86,
  "computedClosingBalance": 7063.95,
  "closingBalance": 7063.95,
  "difference": 0,
  "balanceCheck": "verified",
  "balanceCheckPassed": true,
  "badRows": [],
  "charged": true,
  "exportFiles": [
    {"format": "quickbooks", "key": "sample-statement-cd346ae9-quickbooks.csv", "url": null},
    {"format": "xero", "key": "sample-statement-cd346ae9-xero.csv", "url": null},
    {"format": "ofx", "key": "sample-statement-cd346ae9.ofx", "url": null}
  ]
}
```

And its first transactions:

| date | description | amount | balance |
|---|---|---:|---:|
| 2025-01-01 | Check 1650 | -1149.46 | 2456.63 |
| 2025-01-01 | BRIGHTPATH ELECTRIC AUTOPAY WEB ID 437965622 | -237.18 | 2219.45 |
| 2025-01-02 | HEARTH GAS CO AUTOPAY WEB ID 312706058 | -169.22 | 2050.23 |
| 2025-01-03 | ONLINE TRANSFER TO SAVINGS XXXXXX9884 REF #IBXWDE4PUH | -572.25 | 1477.98 |

The QuickBooks file starts `Date,Description,Amount` / `01/01/2025,Check 1650,-1149.46`.

### Input

| Field | What it does |
|---|---|
| **Statement URLs** (`urls`) | Direct links to statement PDFs or bank CSV exports, one per line (at most 1,000 per run). |
| Also write accounting files (`exportFormats`) | `quickbooks`, `xero` and/or `ofx`: one file of each per statement, in the run's key-value store. Default none. |
| Date order (`dateOrder`) | `auto` (default), `MDY` (month first, US) or `DMY` (day first, UK, EU, AU). |
| Statement type (`statementType`) | `auto` (default), `bank` or `card`. Decides signs on PDFs and the OFX account type. |
| Currency (`currency`) | A 3-letter code such as `GBP`, used instead of the one on the statement. |
| Flip CSV signs (`invertCsvSigns`) | For CSV exports that show money out as positive numbers. Default off. |
| Max pages per statement (`maxPagesPerStatement`) | Refuse longer PDFs, uncharged: 1 to 200, default 100. |
| Max statements per run (`maxStatements`) | Stop after this many statements. |

```json
{
  "urls": [
    "https://example.com/statements/checking-2026-09.pdf",
    "https://example.com/statements/card-export-2026-09.csv"
  ],
  "exportFormats": ["quickbooks", "ofx"],
  "dateOrder": "DMY",
  "currency": "GBP",
  "maxStatements": 50
}
```

### Output

One summary row per statement, then one row per transaction, in one dataset. Fields a row type doesn't have are
left out; fields the statement doesn't print are `null`.

A transaction row:

```json
{
  "rowType": "transaction",
  "statementId": "sample-statement-cd346ae9",
  "sourceUrl": "https://whitehatharbor.com/statements/sample-statement.pdf",
  "fileName": "sample-statement.pdf",
  "accountLast4": "1544",
  "currency": "USD",
  "index": 2,
  "date": "2025-01-01",
  "postedDate": null,
  "description": "BRIGHTPATH ELECTRIC AUTOPAY WEB ID 437965622",
  "amountCents": -23718,
  "amount": -237.18,
  "balanceCents": 221945,
  "balance": 2219.45,
  "checkNumber": null,
  "flags": [],
  "page": 1,
  "line": 11
}
```

A summary row also has `sourceUrl`, `fileType` (`pdf` or `csv`), `statementIndex` and `statementsInFile` (for PDFs
holding several accounts), `accountName`, `computedClosingBalanceCents`, `differenceCents`, `dateOrder`, `warnings`
(what to double-check, e.g. a statement that prints no year) and `rowErrors` (CSV rows that couldn't be imported,
with their row number).

**Only the transactions:** in the Output tab pick the **Transactions** view, or from the API add
`?fields=date,description,amount,balance` and filter `rowType` in your spreadsheet. The accounting files hold only
the transactions.

The run's `RUN_STATS` record has every link with what it gave or why it failed (`queries`, failed ones also in
`queriesFailed`), the links not read because a cap was reached (`queriesSkipped`), and counts of statements verified,
mismatched and without a balance.

### The balance check

The converter adds up every transaction and compares the result with the statement's own balances:

- **`verified`**: opening balance + every transaction = closing balance, to the cent, and every running balance
  printed on a row follows from the row before. Nothing is missing or misread.
- **`mismatch`**: they don't add up. `difference` says by how much, and `badRows` lists the transactions where the
  running balance stops agreeing (those rows also carry the `balance-break` flag). Usually a row was misread or the
  file is incomplete. **Not charged**; the rows still come back.
- **`unavailable`**: the statement shows no balances to check against, as with most card CSV exports.

All money is handled as whole cents, never as floating-point numbers, so the check is exact. For CSV exports with a
running-balance column, the opening balance is the first row's balance minus its amount.

### What it reads, and what it doesn't

- **PDFs with a text layer**, which is what banks and card issuers send and what online banking downloads. Several
  accounts in one PDF come back as separate statements. Statements that print no year use the statement period; MM/DD
  and DD/MM are told apart from the dates themselves, or set with **Date order**.
- **Bank CSV exports** (also semicolon- and tab-separated, with comma decimals, UTF-16 or Windows-1252): one signed
  amount column, separate debit and credit columns, or an amount with a type column (debit/credit). The header row is
  found in the first 15 rows. Dates that fit both MM/DD and DD/MM are refused until you set **Date order**; nothing is
  guessed.
- **Not read:** scanned or photographed statements (no OCR), password-protected PDFs, Excel files (export CSV
  instead), OFX/QFX/MT940/CAMT files, and links to a bank's login or viewer page.

The engine is tested on synthetic statements in 25 layout families (invented banks, people and merchants), not on
every real bank's layout. In its blind tests no statement that passed the balance check had a wrong amount, date or
row (0 of 327). When a statement reads wrong, the check is what tells you, and that statement isn't charged.

#### Run it on a schedule, or from your own code

1. Save your input as a [task](https://docs.apify.com/platform/actors/running/tasks) (**Save as a new task**, top right
   of the actor page). Every run converts, and charges for, every statement you list, so a monthly schedule fits a
   link that's replaced each month.
2. Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the
   [API](https://apify.com/humble-echidna/bank-statement-converter/api)
   (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with
   your API token); let a [webhook](https://docs.apify.com/platform/integrations/webhooks) tell your system when a run
   succeeds; or connect it to Make, Zapier or n8n through
   [Apify's integrations](https://docs.apify.com/platform/integrations).

#### Can I use Bank Statement Converter from an AI agent (MCP)?

Yes, through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp), from Claude, ChatGPT, Cursor or
any other MCP client. Add this to your client's MCP configuration (or let the agent find it with the server's actor
search); your client signs you in to Apify:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=humble-echidna/bank-statement-converter"
    }
  }
}
```

To use an [Apify API token](https://console.apify.com/settings/integrations) instead of signing in, add
`"headers": {"Authorization": "Bearer <APIFY_TOKEN>"}` next to `url`.

The agent passes statement links, e.g. `{"urls": ["https://example.com/statement.pdf"]}`, and should read
`balanceCheck` on the summary row before trusting the transactions.

### Who it's for

Bookkeepers and accountants who get clients' statements as PDFs every month and need them in QuickBooks, Xero or a
spreadsheet; lenders and underwriters checking income and spending from statements; developers and AI agents that
need statement transactions as data, with a check they can rely on.

### Why this one?

- **A balance check on every statement**, to the cent, with the rows where it breaks, and **no charge when it
  fails**.
- **One flat price per statement**, whatever its length (up to 200 pages): a 30-page business statement costs the
  same as a 2-page one.
- **QuickBooks Online, Xero and OFX files** written by the same engine, not just a CSV to re-map yourself.
- **Exact money:** amounts in whole cents as well as decimals, so totals never drift by a cent.
- **No guessing:** ambiguous dates, rows without a direction and CSV rows that don't parse are reported, not quietly
  filled in or dropped.
- **Polite and safe.** It identifies itself honestly (User-Agent `HumbleEchidnaApify`), follows each site's
  robots.txt, and only requests public web addresses on the standard ports (80 and 443).

### Limits

- No OCR: scanned statements are reported as such, not charged.
- No password-protected PDFs: save an unprotected copy first.
- At most 200 pages per PDF (100 by default) and 30 MB per file; at most 1,000 links per run.
- One link is read at a time.
- CSV exports without a currency symbol or code are read as `USD` unless you set **Currency**.
- Bank and card statements only: not investment, loan or payroll statements.

### Privacy and your files

Your files are downloaded and read inside your own run; the results go to your run's storage in your Apify account,
which keeps them for your plan's data retention period. The converter sends your data nowhere else and keeps nothing
between runs. Links you give must open without a login; for private statements, use a link that expires (a signed
URL).

### FAQ

#### Can it read scanned statements?

No. A PDF without a text layer is reported as scanned (`"error": "Scanned"` for its link in `RUN_STATS`) and not
charged. Most
banks' downloaded statements have a text layer; a scan or a phone photo doesn't.

#### Why does my statement fail the balance check?

Usually a row was read wrong or the PDF is incomplete (a page missing, two months in one file). `badRows` points at
the first transactions where the running balance stops adding up, so you can compare them with the PDF. Failed
statements aren't charged. If a statement from your bank fails every month, please open an issue (without the
statement itself) saying which bank and what the rows look like.

#### My dates came out wrong, or a CSV was refused for its dates.

Set **Date order** to `DMY` for day-first dates (UK, Europe, Australia) or `MDY` for month-first (US). A CSV whose
dates all fit both orders (every day 12 or lower) is refused until you choose, rather than guessed.

#### Something that used to work now fails. Why?

Banks change their statement layouts now and then. The balance check and the run log say which statement and which
rows; please open an issue with the bank's name and what changed.

#### Is it legal to convert bank statements?

Yes: it converts files you provide, which you have the right to use. It doesn't log in to any bank or scrape any site;
it only downloads the links you give it, following the site's robots.txt. Statements contain personal financial data:
only convert statements you're entitled to handle.

### Feedback and support

Found a bug, or need a field or a bank's layout that isn't read well? Open an issue on the **Issues** tab with the
input you used (never attach a real statement; describe its layout instead).

### Versions

Current version: **0.1**. See the Changelog tab for what changed in each version.

### Engine notes

For maintainers. The conversion is the Cleanrow engine (`mms/statements/engine/src`), copied unchanged into
`src/engine/` by `npm run vendor-engine` (origin and commit in `src/engine/MANIFEST.json`). Changes the actor would
like upstream, not made here:

- `addCents` isn't exported from the engine's index; the actor imports it from `money.ts` directly.
- A CSV export has no `StatementResult` builder in the engine, so the actor assembles one (period from the dates,
  balances from the running-balance check). An engine `statementFromCsv` would keep hosts from each writing their own.
- `prepareCsv` falls back to `USD` when a file names no currency and doesn't say it fell back, so the actor can't warn
  about it; a `currencySource` (column, symbol or default) on `PreparedCsv` would let it.
- Parsing runs in a worker stopped after 120 s.

# Changelog

This Actor's version history is a separate document: https://apify.com/humble-echidna/bank-statement-converter/changelog.md

# Actor input Schema

## `urls` (type: `array`):

Direct links to your statements, one per line: bank or credit card statement PDFs (with a text layer, as banks send them; scans aren't read) or CSV exports from your bank. Public web addresses only (http/https, ports 80 and 443), at most 1,000 per run. The file type is read from the file itself. The default is one synthetic sample statement (invented bank and account).

## `exportFormats` (type: `array`):

Optional files written per statement to the run's key-value store, ready to import: `quickbooks` (QuickBooks Online bank CSV: Date, Description, Amount), `xero` (Xero bank statement CSV) and `ofx` (OFX 2.2). Each statement's summary row links its files. Default none: the results table already downloads as CSV, Excel or JSON. Free.

## `dateOrder` (type: `string`):

How numeric dates like 03/04/2026 are read: `auto` (default) works it out from the dates and the statement period; `MDY` is month first (US), `DMY` day first (UK, Europe, Australia). Set it when a CSV export's dates all fit both orders: those files are refused, not guessed.

## `statementType` (type: `string`):

`auto` (default) detects bank account or credit card statements from the PDF. Set `bank` or `card` to skip detection; it decides signs on PDFs (card statements print charges as positive) and whether OFX files are bank or card files. CSV exports are marked `unknown` unless set.

## `currency` (type: `string`):

Optional 3-letter ISO code, e.g. `GBP` or `EUR`, used instead of the one read from the statement. Set it for CSV exports that show no currency symbol or code: those are read as `USD`. Leave empty (the default) to use what the statement shows.

## `invertCsvSigns` (type: `boolean`):

CSV exports only: flip every amount's sign, for exports that show money out as positive numbers (common for credit cards). Default false. In the output, money out is always negative. PDFs are never flipped.

## `maxPagesPerStatement` (type: `integer`):

Refuse PDFs with more pages than this, before reading them: 1 to 200, default 100. A refused file is reported and not charged. Raise it for long business statements; split longer files.

## `maxStatements` (type: `integer`):

Stop after this many statements in total, e.g. 20 (a PDF holding several accounts counts each account). Minimum 1; leave empty (the default) for no limit. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first.

## Actor input object example

```json
{
  "urls": [
    "https://whitehatharbor.com/statements/sample-statement.pdf"
  ],
  "exportFormats": [],
  "dateOrder": "auto",
  "statementType": "auto",
  "invertCsvSigns": false,
  "maxPagesPerStatement": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `files` (type: `string`):

No description

## `runStats` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://whitehatharbor.com/statements/sample-statement.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/bank-statement-converter").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": ["https://whitehatharbor.com/statements/sample-statement.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("humble-echidna/bank-statement-converter").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://whitehatharbor.com/statements/sample-statement.pdf"
  ]
}' |
apify call humble-echidna/bank-statement-converter --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,humble-echidna/bank-statement-converter"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/19dwhRBu4ch3QloLd/builds/YbIMAoCiTkMgejQif/openapi.json
