# HTML Tables to Markdown (GFM) for RAG & LLMs (`awesome_highboy/tableforge`) Actor

Extract every HTML table from any URL into clean, deterministic GitHub-Flavored Markdown (GFM). Auto-detects headers (or synthesizes col1..N), escapes pipes, collapses whitespace, and stamps each table with an sha256 hash for dedup & idempotency. RAG / embeddings / LLM ready. Same HTML, same output.

- **URL**: https://apify.com/awesome\_highboy/tableforge.md
- **Developed by:** [Adam](https://apify.com/awesome_highboy) (community)
- **Categories:** Developer tools, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 table 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TableForge: Docs -> Queryable GFM Tables

Turn the HTML tables buried in your pages into clean, deterministic, RAG-ready GitHub-Flavored Markdown.

### What it does

TableForge fetches each URL you provide, parses the returned HTML with a real DOM (jsdom), and extracts **every** `<table>` on the page. Each table is converted into a clean GitHub-Flavored Markdown (GFM) table:

- Cell text is whitespace-collapsed and trimmed, and `|` characters are escaped so the Markdown stays valid.
- If the table has a header row (`<thead>` or a leading `<th>` row), those headers are used; otherwise synthetic headers (`col1`, `col2`, ...) are generated so every table is well-formed.
- A standard GFM header separator (`| --- | --- |`) is emitted, making the output ready to drop into Markdown, paste into an LLM prompt, or feed an embeddings/RAG pipeline.
- Every table gets a deterministic `content_hash` (`sha256:` + 64 hex) computed over its GFM text, so identical tables always produce identical hashes for dedup and idempotency.

The conversion is fully deterministic: the same HTML in always yields the same Markdown and the same hash out. Nothing is summarized, rewritten, or hallucinated; missing cells are emitted as empty, never invented.

### Input

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `urls` | array of strings | yes | Page URLs whose tables you are authorized to extract (your own, authorized, or public pages). |
| `ownership_attestation` | boolean | yes | Must be `true` to confirm you own or are authorized to extract from these pages. If `false`/omitted, the run is rejected before any work and bills `$0`. |

### Output

Records are pushed to the dataset. There are two `record_type` values:

**`table`** — one record per extracted table:

| Field | Type | Description |
| --- | --- | --- |
| `record_type` | string | `"table"` |
| `source_url` | string | The page the table came from. |
| `table_index` | integer | Zero-based index of the table within the page. |
| `gfm_table` | string | The full GitHub-Flavored Markdown table. |
| `column_headers` | string\[] | The header cells (real or synthesized `col1..colN`). |
| `row_count` | integer | Number of body rows (excluding the header). |
| `content_hash` | string | Deterministic `sha256:<64 hex>` over the GFM text. |

**`run_summary`** — one record per run:

| Field | Type | Description |
| --- | --- | --- |
| `record_type` | string | `"run_summary"` |
| `pages_processed` | integer | Pages successfully fetched and scanned. |
| `tables_extracted` | integer | Total tables converted across all pages. |

A page that fails to fetch is skipped entirely (it contributes no records and is not counted). A page that fetches successfully but contains no tables is counted in `pages_processed` but adds no table records. Either way, pages with zero tables are **never** billed.

### Pricing

TableForge uses Apify Pay-Per-Event. You are billed only for:

| Event | Price (USD) | When it fires |
| --- | --- | --- |
| `actor_run_start` | $0.005 | Once per run, after the ownership and paid-plan gates pass. |
| `table_extracted` | $0.001 | Once per table successfully converted to GFM (the billed unit). |

Pages are scanned but **not** billed (no double-charging), and failed or empty pages cost $0.

**Example run:** scan 10 pages that together contain 80 tables -> `$0.005` (run start) + `80 x $0.001` = **$0.085 total**.

### Why this Actor

- **Deterministic + idempotent.** Output is a pure function of the input HTML. Every table carries an `sha256:` content hash over its Markdown, so you can dedup, cache, and detect changes reliably across runs.
- **No hallucination.** Tables are parsed structurally from the DOM and reproduced faithfully. Missing cells are emitted empty, never fabricated; nothing is paraphrased or summarized.
- **Ownership attestation gate.** A run cannot proceed unless you attest you are authorized to extract from the pages; without it, the run is rejected before any work with zero billing.
- **Embeddings/RAG-ready by design.** Clean GFM with preserved (or synthesized) headers, escaped pipes, and per-table hashes drops straight into LLM prompts, vector stores, and Markdown docs.

***

*This Actor is AI-authored and operated under the publisher's LLC. It uses Apify's `Actor.charge()` solely to **bill the customer** for the events above; the Actor contains no payout or money-out capability of any kind.*

# Actor input Schema

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

URLs whose tables you are authorized to extract.

## `ownership_attestation` (type: `boolean`):

I own/am authorized to extract from these pages (REQUIRED)

## Actor input object example

```json
{
  "urls": [
    "https://www.w3.org/TR/html401/struct/tables.html"
  ],
  "ownership_attestation": true
}
```

# 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://www.w3.org/TR/html401/struct/tables.html"
    ],
    "ownership_attestation": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("awesome_highboy/tableforge").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://www.w3.org/TR/html401/struct/tables.html"],
    "ownership_attestation": True,
}

# Run the Actor and wait for it to finish
run = client.actor("awesome_highboy/tableforge").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://www.w3.org/TR/html401/struct/tables.html"
  ],
  "ownership_attestation": true
}' |
apify call awesome_highboy/tableforge --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=awesome_highboy/tableforge",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/fS4ScuVbHU4ly2LdX/builds/7gDNPojgqpZBhQr24/openapi.json
