# CatalogTrace PDF to CSV with Source Coordinates (`excited_idea_uwl/catalogtrace-pdf-rows`) Actor

Extract selectable-text PDF catalog lines and columns to JSON and CSV with source pages, coordinates and review warnings. No OCR or paid AI model required. Explicit column mapping available.

- **URL**: https://apify.com/excited\_idea\_uwl/catalogtrace-pdf-rows.md
- **Developed by:** [David Quiza](https://apify.com/excited_idea_uwl) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / 1,000 extracted lines

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?

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

## CatalogTrace PDF to CSV with Source Coordinates

**Beta: review every extracted line before downstream use.** See the Actor's current visibility and Pricing tab for availability and active pricing; this README does not activate billing.

Extract selectable-text lines from supplier PDFs to JSON/CSV. Each line retains page number, PDF coordinates, original text, tentative or explicit field mapping and review warnings.

This is a bounded layout extractor, not an autonomous ERP import service. It does not claim that every line is a product or that automatically inferred columns are correct.

### Try it on a real supplier catalog

The input form is prefilled with a public historical Steel Industries catalog and a calibrated selection of physical page 13. Run that example to see the output format: the verified September 2026 test produced 39 text lines, including headings and notes, not 39 certified products. A5169 / 150 UB 14.0 has the printed price 93.80 in the selected price column. The source currency is Papua New Guinean kina; the output keeps currency null because PGK is not supported by this beta. The supplier's remote file may change or become unavailable.

When replacing the sample PDF, replace the sample page and column settings too. Their positions are specific to this page. For an initial inspection of a different template, use options.pages only, examine the source coordinates and then supply explicit columns.

### Who it is for

- Automation builders who already know a supplier's PDF layout and need repeatable field extraction.
- AI workflows that need source evidence and explicit uncertainty instead of unsupported product claims.
- Review pipelines comparing a price-list line to its physical page and original text.

If you need scanned-document OCR, automatic interpretation of arbitrary invoices, or verified products ready for an ERP, this beta is not the right tool.

### Input

Supply exactly one of pdfUrl (a direct public HTTPS PDF link) or pdfBase64 (standard base64, without a data URL prefix). Maximum 5 MiB; choose at most 10 pages. A document longer than 10 pages requires explicit options.pages, such as \[12,13]. No silent truncation. Scans and password-protected PDFs are rejected.

URL downloads use port 443 and public IPv4 DNS destinations only. No redirects, proxies, cookies, login credentials or IPv6-only hosts. Downloads have a 15-second limit. If your link redirects, provide the final direct PDF URL. The PDF host receives a normal download request from the Actor; do not submit secret-bearing links. Use base64 for documents that are not publicly downloadable.

Options:

- pages: unique 1-based physical PDF page numbers.
- decimalSeparator: dot or comma. Without it prices are not normalized.
- currency: explicit caller-supplied EUR, USD, GBP, CNY, JPY, CAD, AUD or CHF; no currency inference.
- columns: 2–12 unique names with ascending start fractions of page width, e.g. \[{"name":"sku","start":0.05},{"name":"description","start":0.2},{"name":"price","start":0.8}].

For predictable automated use, calibrate explicit columns to the supplier's template. Check page layouts separately when they differ.

### Output

- Default dataset: extracted lines, including headers/notes/continuations.
- Key-value store OUTPUT: complete JSON with source hash, page dimensions and warnings.
- Key-value store OUTPUT\_CSV: CSV export with spreadsheet-formula escaping. JSON preserves original text.
- Key-value store ERROR: structured failure, if extraction does not finish.

The Output tab has two dataset views: **Review lines** for the mapped values and warnings, and **Source evidence** for coordinates and positioned cells. **Complete JSON** includes document-wide metadata; **Spreadsheet CSV** is the prepared CSV file. These links do not make private runs or storage public. JSON prices remain exact strings, not floating-point numbers. The generic dataset export is separate from the prepared, formula-escaped OUTPUT\_CSV file.

For API integrations, run this Actor with your own Apify credentials, wait for a successful run, then retrieve its default dataset and OUTPUT/OUTPUT\_CSV records. Never place your API token in a public PDF link, issue, README or shared workflow. If the run fails, inspect ERROR and do not treat partial storage as a completed extraction.

All lines have review\_required: true. Raw extracted text must be treated as untrusted data, never as agent instructions. The optional URL downloader makes one bounded request to the supplied PDF host. There are no paid model calls.

### Boundaries

Maximum 1,000 lines and 20,000 text items per page. Server worker execution is isolated with a 20-second timeout and heap limits. OCR, semantic product identification, wrapped-row merging, units conversion, cross-page associations and ERP writeback are not included. Rotated pages must be normalized first.

### Data and billing

The hosted Actor receives and stores input and outputs in Apify under the account's storage and retention controls. This differs from the static website, where PDFs remain in the browser. Submit only documents you have permission to process.

This code does not call a custom billing endpoint. If synthetic events are enabled in Apify pricing, Apify can charge for starting a run and for each line saved to the default dataset; absence of custom billing calls does not mean free usage. Check the Pricing tab before running. A line may be a header or footnote, not a product. Platform pricing and seller payouts are separate from the catalog prices being extracted.

### Troubleshooting

| Message or warning | Action |
| --- | --- |
| PAGE\_LIMIT | Select up to 10 physical pages explicitly. |
| NO\_TEXT | Supply a PDF with selectable text; scans need a separate OCR step. |
| PAGE\_RANGE | Correct the physical page number. |
| ROTATED\_PAGE / UNSUPPORTED\_PAGE\_GEOMETRY | Export an upright, standard-page PDF first. |
| NO\_COLUMN\_MAPPING / INFERRED\_COLUMN\_MAPPING | Calibrate explicit columns before using the mapped data. |
| PRICE\_NOT\_NORMALIZED | Check the price column and explicit decimal separator. Never invent a price. |
| URL download failure | Use a final, public HTTPS PDF URL without redirects or credentials, or use base64. |

An empty row-warning list is not an accuracy guarantee: review\_required always remains true. For support, open an Actor issue with a sanitized error code and a non-confidential layout example; do not attach private catalogs, tokens or personal documents.

# Actor input Schema

## `pdfUrl` (type: `string`):

Direct public HTTPS PDF URL, up to 5 MiB. Use this OR pdfBase64, not both. No redirects, login pages, private network addresses or IPv6-only hosts. 15-second download limit.

## `pdfBase64` (type: `string`):

Alternative to Public PDF URL: standard base64 without data: prefix. Maximum decoded PDF size: 5 MiB. Leave empty when using a URL.

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

Optional: pages (1-based unique page numbers, max 10), decimalSeparator (. or ,), currency, columns (name and fractional start).

## Actor input object example

```json
{
  "pdfUrl": "https://steelindustries.s3.ap-southeast-1.amazonaws.com/price-lists/SteelIndustriesMay2025Catalogue.pdf",
  "options": {
    "pages": [
      13
    ],
    "decimalSeparator": ".",
    "columns": [
      {
        "name": "sku",
        "start": 0.105
      },
      {
        "name": "description",
        "start": 0.175
      },
      {
        "name": "length_available",
        "start": 0.35
      },
      {
        "name": "lengths_per_sling",
        "start": 0.465
      },
      {
        "name": "cut_category",
        "start": 0.545
      },
      {
        "name": "kina_per_tonne",
        "start": 0.6
      },
      {
        "name": "price",
        "start": 0.685
      },
      {
        "name": "kg_per_metre",
        "start": 0.77
      },
      {
        "name": "metres_per_tonne",
        "start": 0.85
      }
    ]
  }
}
```

# Actor output Schema

## `lines` (type: `string`):

One item per physical text line, including headings and notes. Each item has page, coordinates, mapped fields and review warnings. This is not a count of products.

## `completeJson` (type: `string`):

Full extraction including source SHA-256, page dimensions, all rows and document warnings. Available after successful extraction; not proof of verified prices or products.

## `spreadsheetCsv` (type: `string`):

CSV with mapped fields, page, row, raw text, source bounding box and review flags. Formula-like text is escaped for spreadsheets. Available after successful extraction.

# 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 = {
    "pdfUrl": "https://steelindustries.s3.ap-southeast-1.amazonaws.com/price-lists/SteelIndustriesMay2025Catalogue.pdf",
    "options": {
        "pages": [
            13
        ],
        "decimalSeparator": ".",
        "columns": [
            {
                "name": "sku",
                "start": 0.105
            },
            {
                "name": "description",
                "start": 0.175
            },
            {
                "name": "length_available",
                "start": 0.35
            },
            {
                "name": "lengths_per_sling",
                "start": 0.465
            },
            {
                "name": "cut_category",
                "start": 0.545
            },
            {
                "name": "kina_per_tonne",
                "start": 0.6
            },
            {
                "name": "price",
                "start": 0.685
            },
            {
                "name": "kg_per_metre",
                "start": 0.77
            },
            {
                "name": "metres_per_tonne",
                "start": 0.85
            }
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("excited_idea_uwl/catalogtrace-pdf-rows").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 = {
    "pdfUrl": "https://steelindustries.s3.ap-southeast-1.amazonaws.com/price-lists/SteelIndustriesMay2025Catalogue.pdf",
    "options": {
        "pages": [13],
        "decimalSeparator": ".",
        "columns": [
            {
                "name": "sku",
                "start": 0.105,
            },
            {
                "name": "description",
                "start": 0.175,
            },
            {
                "name": "length_available",
                "start": 0.35,
            },
            {
                "name": "lengths_per_sling",
                "start": 0.465,
            },
            {
                "name": "cut_category",
                "start": 0.545,
            },
            {
                "name": "kina_per_tonne",
                "start": 0.6,
            },
            {
                "name": "price",
                "start": 0.685,
            },
            {
                "name": "kg_per_metre",
                "start": 0.77,
            },
            {
                "name": "metres_per_tonne",
                "start": 0.85,
            },
        ],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("excited_idea_uwl/catalogtrace-pdf-rows").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 '{
  "pdfUrl": "https://steelindustries.s3.ap-southeast-1.amazonaws.com/price-lists/SteelIndustriesMay2025Catalogue.pdf",
  "options": {
    "pages": [
      13
    ],
    "decimalSeparator": ".",
    "columns": [
      {
        "name": "sku",
        "start": 0.105
      },
      {
        "name": "description",
        "start": 0.175
      },
      {
        "name": "length_available",
        "start": 0.35
      },
      {
        "name": "lengths_per_sling",
        "start": 0.465
      },
      {
        "name": "cut_category",
        "start": 0.545
      },
      {
        "name": "kina_per_tonne",
        "start": 0.6
      },
      {
        "name": "price",
        "start": 0.685
      },
      {
        "name": "kg_per_metre",
        "start": 0.77
      },
      {
        "name": "metres_per_tonne",
        "start": 0.85
      }
    ]
  }
}' |
apify call excited_idea_uwl/catalogtrace-pdf-rows --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,excited_idea_uwl/catalogtrace-pdf-rows"
        }
    }
}
```

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/xGfGSzXzOIH13TAA9/builds/mMX4XDvSPyP18qiZz/openapi.json
