# CJK Invoice Extractor + Tax ID Check (China, Taiwan, Japan) (`robertwang/cjk-invoice-extractor`) Actor

Invoice reconciliation for CN/TW/JP invoices and receipts: extract to JSON and Excel, validate China USCC, Taiwan tax ID and Japan T-number checksums, and verify per-rate tax lines.

- **URL**: https://apify.com/robertwang/cjk-invoice-extractor.md
- **Developed by:** [Robert Wang](https://apify.com/robertwang) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 invoice extracteds

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

## CJK Invoice Extractor + Tax ID Check (China, Taiwan, Japan)

Turn **Chinese, Taiwanese and Japanese invoices and receipts** (PNG, JPEG, WEBP, TIFF or PDF) into **structured JSON and an Excel file**, with the seller's **tax ID checked against its official check-digit algorithm** and the **tax math verified line by line**.

Built for accounts payable, bookkeeping and expense teams that receive documents such as 发票 / 统一发票 / 電子發票 / 收據 / 請求書 / 適格請求書 (インボイス) / 領収書 and need clean data they can trust before importing it.

> Need general CJK documents (contracts, forms, letters) rather than invoice reconciliation? See [CJK Document Extractor](https://apify.com/robertwang/cjk-document-extractor).

### What it does

- **Reads the document with a vision AI model** and returns: country, document type, invoice number, issue date (民國 and 令和 years converted to ISO dates), vendor, buyer, seller tax ID, buyer tax ID, currency, line items, subtotal, **one tax line per tax rate**, total tax and total amount.
- **Validates the seller tax ID offline**:
  - 🇨🇳 China **Unified Social Credit Code** (统一社会信用代码, 18 characters, GB 32100-2015 mod-31 check character)
  - 🇹🇼 Taiwan **統一編號** (8 digits, weighted mod-5 rule, including the special case when the 7th digit is 7)
  - 🇯🇵 Japan **qualified invoice registration number** (登録番号, `T` + 13-digit corporate number with check digit)
- **Runs 7 consistency checks** on every document (see below) and sets `needsReview: true` when any check fails.
- **Re-reads only what failed, once**: if the tax ID fails its checksum, the header is cropped, zoomed 2x and re-read by a stronger model. If the amounts do not add up, the totals area is re-read the same way. A re-read value is kept only if it passes more checks; every re-read is logged in `reextraction`.
- **Handles mixed tax rates**: Japan 10% + 8% (reduced rate), China 13% / 9% / 6%, Taiwan 5%.
- **Excel output**: `invoices.xlsx` with three sheets (`invoices`, `line_items`, `tax_lines`).
- **Validate-only mode**: paste a list of tax IDs and get a checksum result for each. No AI is used, so it is fast and very cheap.

### The 7 checks

| Check | Meaning |
| --- | --- |
| `registrationNumberFormatAndChecksum` | Seller tax ID has the right format and check digit for its country |
| `lineItemsSumEqualsSubtotal` | Sum of line amounts = subtotal |
| `taxableSumEqualsSubtotal` | Sum of taxable amounts across tax lines = subtotal |
| `taxLinesSumEqualsTotalTax` | Sum of per-rate tax amounts = total tax |
| `eachTaxLineMatchesRate` | Each tax line ≈ taxable amount × rate (±1 for rounding) |
| `subtotalPlusTaxEqualsTotal` | Subtotal + total tax = total |
| `taxRatesMatchLineItems` | The set of tax rates on the lines matches the tax lines |

A checksum confirms that an ID is *well-formed*; it does not confirm that the company is registered or active. Use the official registries (e.g. Japan's 適格請求書発行事業者公表サイト) when you need that.

### How to use

1. Click **Start** with the default input to run a **free demo** on 3 bundled synthetic invoices (Japanese, Traditional Chinese, Simplified Chinese). The demo is not charged.
2. Add your own files under **Invoice / receipt file URLs** (any public link), or upload files to a key-value store and list their keys under **Uploaded file keys**.
3. Optional: set **Country** if all documents come from one country, and **Max pages per PDF** (default 3).
4. Open the **Output** tab: the table view shows one row per document, and `invoices.xlsx` is in the run's key-value store.

#### Input example

```json
{
  "mode": "extract",
  "fileUrls": ["https://example.com/invoices/2026-10-001.pdf", "https://example.com/receipts/photo.jpg"],
  "country": "auto",
  "outputExcel": true,
  "retryFailedFields": true
}
```

Validate-only:

```json
{ "mode": "validateOnly", "taxIds": ["T1180301018771", "04595257", "91350100M000100Y43"] }
```

Full-width characters, spaces and dashes are normalized before checking.

### Output example

One dataset item per document (shortened):

```json
{
  "source": "https://example.com/invoices/jp-001.png",
  "status": "ok",
  "country": "JP",
  "documentType": "invoice",
  "invoiceNumber": "INV-2026-0142",
  "issueDate": "2026-10-20",
  "vendor": "サンプル商事株式会社",
  "registrationNumber": "T2901234567890",
  "registrationNumberValid": true,
  "currency": "JPY",
  "subtotal": 71320,
  "taxLines": [
    {"rate": 0.1, "taxable_amount": 67000, "tax_amount": 6700},
    {"rate": 0.08, "taxable_amount": 4320, "tax_amount": 346}
  ],
  "totalTax": 7046,
  "totalAmount": 78366,
  "lineItems": [{"description": "Webサイト保守", "qty": 1, "unitPrice": 55000, "amount": 55000, "taxRate": 0.1}],
  "validation": {"allPassed": true, "passed": 7, "total": 7},
  "needsReview": false,
  "flags": [],
  "reextraction": [],
  "modelsUsed": ["gemini-flash-lite-latest"]
}
```

If a file cannot be downloaded or read, you get an item with `status: "error"` and an `error` message, and it is **not charged**.

### Pricing

Pay per event, no subscription:

| Event | Price |
| --- | --- |
| Invoice / receipt extracted (first page included, re-reads included) | $0.02 |
| Extra PDF page read (2nd page onwards) | $0.003 |
| Tax ID checked (validate-only mode) | $0.0005 |

Failed downloads and unreadable files are not charged. The demo run is free. You can cap spending per run with Apify's *maximum cost per run* setting.

### Accuracy, honestly

- The bundled demo invoices are synthetic and digitally rendered; on those, all fields and all 7 checks pass. Real scans and phone photos will produce more errors. That is what the checks, `needsReview` and the targeted re-read are for: review the flagged rows, not every row.
- The model reads only what is printed. Fields that are not on the document are returned as `null`, not guessed.
- Handwritten receipts, very low-resolution photos and multi-invoice pages are not supported well.

### Privacy

Files are processed in memory and sent to a third-party AI API (Google Gemini) only to read them. Nothing is logged except counts. Turn on **Delete uploaded files after the run** to remove your uploads from the key-value store once processed. Do not upload documents you are not allowed to share with a cloud AI service.

### FAQ

**Which languages?** Simplified Chinese, Traditional Chinese, Japanese, and English text on the same documents.

**Does it check whether a company really exists?** No, only the format and check digit. That catches most typos and OCR misreads, which is the main source of bad IDs in extracted data.

**Can I use it from code or automation tools?** Yes, through the Apify API, or via Make, Zapier or n8n integrations.

**Something looks wrong?** Open an issue on the Issues tab with the run ID. Please do not attach confidential documents.

# Actor input Schema

## `mode` (type: `string`):

Extract reads invoice files with a vision model. Validate only checks the format and check digit of tax IDs you paste in.

## `fileUrls` (type: `array`):

Public URLs of PNG, JPEG, WEBP, TIFF or PDF files. Leave empty (and no uploads) to run a free demo on 3 synthetic invoices.

## `uploadStore` (type: `string`):

Optional. A key-value store that holds files you uploaded. Defaults to this run's store.

## `uploadKeys` (type: `array`):

Optional. Record keys of files in the key-value store above.

## `deleteUploadsAfter` (type: `boolean`):

Remove the records listed in Uploaded file keys once they have been processed.

## `country` (type: `string`):

Set this if all documents are from one country. Decides which tax-ID checksum is applied.

## `taxIds` (type: `array`):

Japan T + 13 digits, Taiwan 8-digit 統一編號, or China 18-character USCC. Full-width characters, spaces and dashes are normalized.

## `outputExcel` (type: `boolean`):

Save invoices.xlsx (sheets: invoices, line_items, tax_lines) to the run's key-value store.

## `retryFailedFields` (type: `boolean`):

If the tax ID or the amounts fail validation, re-read only that part once on a 2x zoomed crop with a stronger model. Included in the per-invoice price.

## `maxPages` (type: `integer`):

Only the first N pages of each PDF are read.

## Actor input object example

```json
{
  "mode": "extract",
  "fileUrls": [],
  "deleteUploadsAfter": false,
  "country": "auto",
  "outputExcel": true,
  "retryFailedFields": true,
  "maxPages": 3
}
```

# Actor output Schema

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

No description

## `excel` (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 = {
    "fileUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("robertwang/cjk-invoice-extractor").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 = { "fileUrls": [] }

# Run the Actor and wait for it to finish
run = client.actor("robertwang/cjk-invoice-extractor").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 '{
  "fileUrls": []
}' |
apify call robertwang/cjk-invoice-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,robertwang/cjk-invoice-extractor"
        }
    }
}
```

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/t7QbLOhyvNzVelekR/builds/4V99bRy7bsy5JiYlJ/openapi.json
