# Thai Toolkit for AI Agents: Baht Text, Holidays, VAT, Tax ID (`chatuporn/thai-toolkit-for-ai-agents`) Actor

Fast offline Thai business tools for AI agents and automations: baht amount in words (บาทถ้วน), Buddhist Era dates, BOT bank holidays and business days, VAT 7%, withholding tax, 13-digit tax ID check, Thai word segmentation, RTGS romanisation, address and postcode parsing.

- **URL**: https://apify.com/chatuporn/thai-toolkit-for-ai-agents.md
- **Developed by:** [Chatuporn Duangthong](https://apify.com/chatuporn) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

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

## Thai Toolkit for AI Agents

**One fast, offline Actor with ten Thai business and text tools** that AI agents (via the Apify MCP server), LLM workflows, Make/Zapier/n8n automations and scripts can call over and over:

| Operation | What it does | Thai keywords |
|---|---|---|
| `bahtText` | Amount in words for invoices and receipts, Thai or English | บาทถ้วน, ตัวอักษร, จำนวนเงินเป็นตัวหนังสือ |
| `thaiDate` | Parse Thai dates, convert Buddhist Era ↔ CE, Thai long/short formats, fiscal year | พ.ศ., ค.ศ., วันที่ไทย, ปีงบประมาณ |
| `holidays` | Bank of Thailand bank holidays and government holidays 2024–2027 | วันหยุดธนาคาร, วันหยุดราชการ, วันหยุดชดเชย |
| `businessDays` | Is it a business day? Add N business days, count business days | วันทำการ |
| `vat` | VAT 7% add / extract with correct satang rounding | ภาษีมูลค่าเพิ่ม, VAT 7% |
| `withholdingTax` | Withholding tax by payment type (services 3%, rent 5%, advertising 2% ...) | ภาษีหัก ณ ที่จ่าย, ภ.ง.ด.3, ภ.ง.ด.53 |
| `taxIdCheck` | Check digit and format of a 13-digit tax ID / company number | เลขประจำตัวผู้เสียภาษี, เลขทะเบียนนิติบุคคล |
| `tokenize` | Thai word segmentation | ตัดคำภาษาไทย |
| `romanize` | Thai → Latin letters (RTGS) | ถอดอักษรไทยเป็นโรมัน |
| `address` | Parse a Thai address, validate/fill the postcode, English names | แยกที่อยู่, รหัสไปรษณีย์, ตำบล อำเภอ จังหวัด |

- **Fast and cheap:** no browser, no external calls, all data bundled in the image. Typical run is 2–3 s end to end at 256 MB; the work itself takes well under a second.
- **Deterministic:** the same input always gives the same output, so it is safe for agents to rely on.
- **Batch friendly:** send up to 10,000 items per run. Each item is processed on its own and returned in the same order.
- **You pay only for items that succeed.** Invalid items come back with an error and are not charged.

### Input

```json
{
  "operation": "bahtText",
  "items": [123.5, 21000000, "100.50"],
  "options": { "language": "th" }
}
```

- `operation` (required): one of the operations above.
- `items` (required): array of values. Plain values work for the common case; objects give full control. Fields set in an object item override `options`.
- `options` (optional): defaults for every item.

### Output

One dataset item per input item:

```json
{
  "index": 0,
  "operation": "bahtText",
  "input": 123.5,
  "status": "ok",
  "result": { "amount": "123.50", "baht": 123, "satang": 50, "textTh": "หนึ่งร้อยยี่สิบสามบาทห้าสิบสตางค์", "text": "หนึ่งร้อยยี่สิบสามบาทห้าสิบสตางค์" },
  "error": null
}
```

`status` is `ok` or `invalid`. When it is `invalid`, `result` is `null` and `error` explains what to fix.

***

### Operations and examples

#### `bahtText`: amount in words (บาทถ้วน)

Item: a number or a string (`"1,234.50"`, `"฿1,000"`, Thai digits `"๑๒๓"`), or `{"amount": ..., "language": "th" | "en" | "both"}`.

| Input | `text` |
|---|---|
| `0` | ศูนย์บาทถ้วน |
| `21` | ยี่สิบเอ็ดบาทถ้วน |
| `101` | หนึ่งร้อยเอ็ดบาทถ้วน |
| `1000000` | หนึ่งล้านบาทถ้วน |
| `21000000` | ยี่สิบเอ็ดล้านบาทถ้วน |
| `0.25` | ยี่สิบห้าสตางค์ |
| `"100.50"` | หนึ่งร้อยบาทห้าสิบสตางค์ |
| `-5` | ลบห้าบาทถ้วน |
| `{"amount": 1234.5, "language": "en"}` | One Thousand Two Hundred Thirty-Four Baht and Fifty Satang |

Rules: เอ็ด for a final 1 after higher digits (11, 21, 101, 1,000,001), ยี่สิบ for 20s, ล้าน repeated for very large numbers (ล้านล้าน). Amounts are rounded half-up to satang.

#### `thaiDate`: parse and convert dates

Item: a date string, or `{"date": "...", "twoDigitYear": "auto" | "be" | "ce"}`.

Understands `"21 มิ.ย. 67"`, `"1 มกราคม 2569"`, `"วันที่ 1 ต.ค. 2568"`, `"2569-09-09"` (Buddhist Era ISO), `"2026-09-09"`, `"9/9/2569"` (day/month/year), Thai digits, `"21 June 2024"`. Years ≥ 2400 are read as Buddhist Era. Two-digit years: `auto` reads 50–99 as BE (69 → 2569) and 00–49 as CE (26 → 2026).

Result for `"21 มิ.ย. 67"`:

```json
{ "iso": "2024-06-21", "yearBE": 2567, "weekdayTh": "ศุกร์",
  "thaiLong": "วันศุกร์ที่ 21 มิถุนายน พ.ศ. 2567", "thaiMedium": "21 มิถุนายน 2567",
  "thaiShort": "21 มิ.ย. 67", "thaiNumeric": "21/06/2567", "isoBE": "2567-06-21",
  "english": "Friday, 21 June 2024", "fiscalYearBE": 2567, "fiscalQuarter": 3 }
```

The fiscal year is the Thai government fiscal year (1 October to 30 September).

#### `holidays`: list holidays

Item: a year (`2026` or `2569`), `"2026-04-01/2026-04-30"`, or `{"start": ..., "end": ...}`.
Options: `calendar` = `bank` (default, Bank of Thailand financial-institution holidays) or `government`; `province` to include regional special days; `includeWeekendHolidays` (default true).

```json
{ "operation": "holidays", "items": [2027], "options": { "calendar": "bank" } }
```

Each holiday has `date`, `nameTh`, `nameEn`, `type` (`holiday`, `substitution` or `special`), `calendars`, and where relevant `substitutesFor`, `region` and `provisional`.

#### `businessDays`: business-day maths

Item objects (`action` defaults to `isBusinessDay`; a plain date string also means `isBusinessDay`):

| Action | Fields | Result |
|---|---|---|
| `isBusinessDay` | `date` | `isBusinessDay`, `reason` (holiday name or weekend), `nextBusinessDay` |
| `add` | `date`, `days` (negative goes back) | `result`, `holidaysSkipped` |
| `count` | `start`, `end`, `includeStart`/`includeEnd` (default true, like Excel NETWORKDAYS) | `businessDays`, `weekdayHolidays` |
| `next` / `previous` | `date`, `inclusive` (default true) | `result` |

```json
{ "operation": "businessDays",
  "items": [ { "action": "add", "date": "2026-12-30", "days": 3 },
             { "action": "count", "start": "1 เม.ย. 2569", "end": "30 เม.ย. 2569" } ] }
```

→ `2027-01-06` (skips New Year's Eve, New Year's Day and the weekend) and `18` business days in April 2026.
Business days are Monday–Friday that are not holidays on the chosen calendar. Data covers 1 Jan 2024 – 31 Dec 2027; dates outside return `invalid`.

#### `vat`: VAT 7%

Item: a number (net amount, VAT added) or `{"amount": 107, "mode": "inclusive"}` to extract VAT from a total. `rate` overrides 7.

| Input | amountBeforeVat | vat | totalIncludingVat |
|---|---|---|---|
| `100` | 100.00 | 7.00 | 107.00 |
| `{"amount": 1000, "mode": "inclusive"}` | 934.58 | 65.42 | 1000.00 |
| `21.5` | 21.50 | 1.51 | 23.01 |

Rounding is half-up to 0.01 THB (not banker's rounding), and base + VAT always equals the total.

#### `withholdingTax`: ภาษีหัก ณ ที่จ่าย

Item: `{"amount": 10700, "type": "services", "vatMode": "inclusive"}` → base 10,000, VAT 700, withholding 300, net payment 10,400, form ภ.ง.ด.53.

| `type` | Company payee | Individual payee |
|---|---|---|
| `services` (ค่าบริการ) | 3% | 3% |
| `hireOfWork` (ค่าจ้างทำของ) | 3% | 3% |
| `professionalFees` (วิชาชีพอิสระ) | 3% | 3% |
| `rent` (ค่าเช่า) | 5% | 5% |
| `advertising` (ค่าโฆษณา) | 2% | 2% |
| `transport` (ค่าขนส่ง) | 1% | 1% |
| `nonLifeInsurance` (เบี้ยประกันวินาศภัย) | 1% | – |
| `promotionPrizes` (รางวัล ส่วนลด ส่งเสริมการขาย) | 3% | 3% |
| `contestPrizes` (รางวัลประกวด ชิงโชค) | 5% | 5% |
| `publicEntertainer` (นักแสดงสาธารณะ) | 5% | 5% |
| `royalties` (ค่าลิขสิทธิ์) | 3% | – |
| `interest` (ดอกเบี้ย) | 1% | 15% |
| `dividends` (เงินปันผล) | 10% | 10% |
| `governmentPayer` (หน่วยงานรัฐจ่าย) | 1% | – |
| `foreignCompany` (จ่ายนิติบุคคลต่างประเทศ) | 15% | – |

Other fields: `payeeType` (`company` default, or `individual`), `vatMode` (`none` default, `exclusive`, `inclusive`); tax is withheld on the amount before VAT. `rate` sets a custom rate. `eWithholding: true` applies the reduced 1% e-Withholding rate where it is available. Sources: Revenue Department order ท.ป. 4/2528 (as amended) and Revenue Code ss. 50, 69 tri and 70. Each result includes its legal basis. Below 1,000 THB per contract, withholding is generally not required; the result adds a note when this applies.

#### `taxIdCheck`: 13-digit ID check

Item: `"0105556123456"`, `"0-1055-56123-45-6"`, or `{"id": "..."}`.
It checks the mod-11 check digit and describes the format:

- **Juristic IDs (start with 0)**, `0-PP-T-YYY-NNNNN-C`: registration province (PP), legal form (T: 2 ordinary partnership, 3 limited partnership, 5 company limited, 7 public company), registration year in Buddhist Era (YYY), running number and check digit. `0-99-…` IDs are Revenue Department numbers for entities that are not registered companies.
- **Personal IDs (start with 1–8):** the category only. The ID is masked in the output (`1XXXXXXXX3451`) unless you set `maskPersonalIds: false`.

Nothing is looked up online and nothing is stored apart from the run's own dataset.

#### `tokenize`: Thai word segmentation

Item: text, or `{"text": "...", "separator": "|", "keepWhitespace": false}`.
`"ฉันรักภาษาไทย"` → `["ฉัน", "รัก", "ภาษาไทย"]`. It uses PyThaiNLP's newmm dictionary tokenizer plus all Thai subdistrict, district and province names, so addresses segment well.

#### `romanize`: Thai → RTGS

Item: text, or `{"text": "...", "capitalize": true}`.
`"หาดใหญ่"` → `hat yai`, `"กรุงเทพมหานคร"` → `krung thep maha nakhon`, `"ถนนสุขุมวิท"` → `thanon sukhumwit`. Place names come from the official English names in the address dataset. Other words go through PyThaiNLP's rule-based royin romaniser, with fixes for silent ห and silent final ร. This is automatic RTGS: it is good for search keys, slugs and labels, but it is not a dictionary transcription (for example สวัสดี gives `swatdi`, not `sawatdi`).

#### `address`: Thai address parsing and postcode validation

Item: an address string, or a bare 5-digit postcode to look up.

```json
{ "operation": "address",
  "items": ["99/1 ม.3 ต.บางพลีใหญ่ อ.บางพลี จ.สมุทรปราการ 10110"] }
```

→ houseNumber `99/1`, moo `3`, subdistrict `บางพลีใหญ่` (Bang Phli Yai), district `บางพลี` (Bang Phli), province `สมุทรปราการ` (Samut Prakan), `postcodeValid: false`, `suggestedPostcode: "10540"`.

- **Fields:** houseNumber, moo, village (หมู่บ้าน), building (อาคาร), floor, room, trok, soi, road, subdistrict, district, province, postcode, their English names, and `unparsed` (for example a company name).
- **Address forms:** labelled (ต./อ./จ., แขวง/เขต, full words) and unlabelled (`"12 สีลม บางรัก กรุงเทพ 10500"`).
- **Postcodes:** a missing postcode is filled when it is unique. A wrong postcode is flagged with the right one.
- **Ambiguity:** when an address is ambiguous, `candidates` lists the possible areas.

***

### Data sources and licences

- **Holidays** (`src/data/holidays.json`, verified 2 Nov 2026):
  - **Bank calendar:** the Bank of Thailand's official calendar and notifications (FPG. 8/2566, 12/2566, 4/2567, 5/2567, 31/2568, 26/2569, 37/2569). It matches BOT's published list exactly for 2024–2027.
  - **Government calendar:** Cabinet Secretariat circular นร 0505/ว 497 and Cabinet resolutions as reported on thaigov.go.th and prd.go.th.
  - **Lunar holidays:** Makha, Visakha and Asarnha Bucha use the officially announced dates. They are not computed.
  - **2027 government calendar:** provisional until the Cabinet publishes it, and Royal Ploughing Day 2027 is not yet announced. The 2027 bank calendar is final.
- **Addresses:** [kongvut/thai-province-data](https://github.com/kongvut/thai-province-data) (MIT licence, © 2025 Kongvut Sangkla), 77 provinces, 7,436 subdistricts with postcodes. The licence is in `src/data/LICENSE-address-data.txt`.
- **NLP:** [PyThaiNLP](https://github.com/PyThaiNLP/pythainlp) 5.4 (Apache-2.0), newmm tokenizer and royin romaniser. The image ships only the word, syllable and stopword lists.

### Limits

- **Holiday data:** covers 2024–2027 only. Ad-hoc special holidays announced after 2 Nov 2026 are not included until the data is updated.
- **Southern provinces:** local bank closures for Eid and Chinese New Year are not modelled.
- **Private employers:** their own holiday lists are not modelled.
- **Withholding tax:**
  - It covers common cases for Thai companies and individuals.
  - Tax treaties, foundations and associations (10%), and progressive personal rates are not modelled.
  - Check the current e-Withholding rate for your payment date.
- **Address parsing:** rule-based, built for Thai-language addresses. English addresses are not parsed.
- **Romanisation:** automatic, so expect occasional spelling differences from official RTGS.
- **Not tax or legal advice:** check important filings with the Revenue Department or your accountant.

### Using it from an AI agent (MCP)

Add the Actor to the Apify MCP server (`https://mcp.apify.com?tools=chatuporn/thai-toolkit-for-ai-agents`). The input schema describes each operation, so the model can pick the right one, for example "How many bank business days are there between 1 and 30 April 2026?" or "Write 21,500.75 baht in Thai words for this invoice."

# Actor input Schema

## `operation` (type: `string`):

Which Thai tool to run on every item. bahtText = amount in words; thaiDate = parse/convert dates (BE/CE); holidays = list holidays; businessDays = business-day maths; vat; withholdingTax; taxIdCheck = 13-digit ID checksum; tokenize = Thai word segmentation; romanize = RTGS Latin spelling; address = parse and validate a Thai address.

## `items` (type: `array`):

JSON array, one entry per value to process (max 10,000). Plain values work: numbers for bahtText/vat, date strings for thaiDate/businessDays, years for holidays, ID strings for taxIdCheck, text for tokenize/romanize/address. Use objects for more control, e.g. {"amount": 10700, "type": "services", "vatMode": "inclusive"} or {"action": "add", "date": "2026-12-30", "days": 3}. See README for every field.

## `options` (type: `object`):

Optional settings applied to every item (an object item overrides them). bahtText: language th|en|both. thaiDate: twoDigitYear auto|be|ce. holidays/businessDays: calendar bank|government, province (for regional days). vat: mode exclusive|inclusive, rate. withholdingTax: type, payeeType company|individual, vatMode none|exclusive|inclusive, eWithholding. taxIdCheck: maskPersonalIds. romanize: capitalize.

## Actor input object example

```json
{
  "operation": "bahtText",
  "items": [
    123.5,
    21000000,
    "100.50"
  ],
  "options": {}
}
```

# Actor output Schema

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

Every processed item with its result or error.

## `overview` (type: `string`):

Index, operation, status, input, result and error.

# 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 = {
    "operation": "bahtText",
    "items": [
        123.5,
        21000000,
        "100.50"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("chatuporn/thai-toolkit-for-ai-agents").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 = {
    "operation": "bahtText",
    "items": [
        123.5,
        21000000,
        "100.50",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("chatuporn/thai-toolkit-for-ai-agents").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 '{
  "operation": "bahtText",
  "items": [
    123.5,
    21000000,
    "100.50"
  ]
}' |
apify call chatuporn/thai-toolkit-for-ai-agents --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chatuporn/thai-toolkit-for-ai-agents"
        }
    }
}
```

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/wobbyVpGrgi66WURd/builds/rW1flrzjXlZgL3jDh/openapi.json
