# Office Documents to Markdown (DOCX, PPTX, XLSX) (`far003/office-docs-markdown`) Actor

Give it DOCX, PPTX, XLSX (and ODT, ODP, ODS) URLs, get clean Markdown, plain text, tables as JSON and metadata for each one, ready for LLM and RAG pipelines. Slides with speaker notes, one table per sheet. No OCR. You pay only for documents that were converted.

- **URL**: https://apify.com/far003/office-docs-markdown.md
- **Developed by:** [Francesco Antonio Russo](https://apify.com/far003) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 document converteds

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

## Office Documents to Markdown – DOCX, PPTX, XLSX (and ODT, ODP, ODS) for LLMs and RAG

**Give it links to Word, PowerPoint and Excel files and get clean Markdown, plain text, every table as JSON, speaker notes and metadata back, one record per file.** No browser, no OCR, no API key, no LLM: the files are read directly, so results are fast and the same file always gives the same answer.

**Price: $0.0015 per converted document** (that is $1.50 per 1,000 documents), one flat price per file whether it has 2 pages or 200 slides. Files that cannot be converted are free.

### Who it is for

- **RAG and AI-agent builders** who need Office files as text a model can read, with headings, lists and tables still in place and metadata to cite.
- **Teams that index documents**: intranets, knowledge bases, search, compliance archives, public-sector data portals.
- **Data people** who want the tables of Word files and the sheets of workbooks as JSON, not as a screenshot.
- Scheduled jobs that watch a list of public documents (the `sha256` of each file tells you when one changed).

For PDFs use the sister Actor [PDF to Text & Markdown Extractor](https://apify.com/far003/pdf-to-text-markdown-extractor) (`far003/pdf-to-text-markdown-extractor`): same record style, same price idea. If you send a PDF here you get a free record that says so.

### What it reads

| File | What you get |
|---|---|
| **Word** (`.docx`, `.docm`, `.dotx`) | Headings (from styles and outline levels), paragraphs, bold / italic / strike / super- and subscript, links, bullet and numbered lists (nested, with Word's own numbering, "2.1" legal numbers kept), tables with merged cells, footnotes and endnotes, text boxes, code in monospace fonts, quotes. Tracked changes are read as accepted (deletions out). |
| **PowerPoint** (`.pptx`, `.ppsx`, `.pptm`) | One section per slide, in presentation order: the title, the text in reading order (top to bottom, then left to right), bullets with their levels, tables, charts as a table of their values, SmartArt text, alt text of pictures, and the **speaker notes**. Hidden slides are kept and marked. |
| **Excel** (`.xlsx`, `.xlsm`, `.xltx`) | One table per sheet. Cells show what Excel shows: dates as dates, `12.5%` as `12.5%`, `1234.5` as `1,234.50` where the cell format says so. Formulas use the value Excel saved. Empty rows and empty leading columns are dropped. The row limit is yours to set and the record says when it cut. |
| **OpenDocument** (`.odt`, `.odp`, `.ods`) | The same output as the three above. Spreadsheet cells carry their displayed text. |

The type is recognised from the **content** of the file (the ZIP package and its content types), never from the extension or the `Content-Type` the server sends: a DOCX served as `report.pdf` is still read as a DOCX.

### Quick start

1. Paste the links into **Document URLs**.
2. Click **Start**.
3. Open the **Overview** tab for the list, **Markdown and text** for the content, **Tables (JSON)**, **Slides and notes**, or download the dataset as JSON, CSV or Excel.

#### Example input

This is the real example prefilled in the input form:

```json
{
  "urls": [
    "https://www.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
    "https://raw.githubusercontent.com/apache/poi/trunk/test-data/slideshow/SampleShow.pptx",
    "https://www.irs.gov/pub/irs-soi/23dbalabama.xlsx"
  ]
}
```

Run result: `3 records (3 ok), 3 charged` = 3 × $0.0015 = **$0.0045**.

#### Example output (a real run, shortened)

A Word file (a US EPA template, 29 pages, 38 tables). Every field is always present; fields that do not apply are `null`:

```json
{
  "url": "https://www.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
  "status": "ok",
  "error": null,
  "charged": true,
  "httpStatus": 200,
  "fileName": "drinking-water-erp-template.docx",
  "sizeBytes": 217904,
  "sha256": "7f5e54a224a47ffa81cf1269996d7847c531cf1051d5fc1dc9510d12b95f7ea3",
  "format": "docx",
  "title": "Emergency Response Plan Template",
  "titleSource": "metadata",
  "language": "en-US",
  "createdAt": "2025-05-19T13:39:00.000Z",
  "modifiedAt": "2025-05-19T17:27:00.000Z",
  "application": "Microsoft Office Word",
  "pageCount": 29,
  "tableCount": 38,
  "imageCount": 0,
  "wordCount": 5407,
  "hasMacros": false,
  "truncated": false,
  "warnings": []
}
```

Its `markdown` field (an excerpt of the real output):

```markdown
#### Utility Information

| **PWSID** | *123456* |
| --- | --- |
| **Owner** | *Town of Anytown* |
| **Total population served and total service connections** | *7,500 served - 2,500 connections* |

☐ Site plans and “as built” drawings for the following components of your system (as applicable):

    - Pumping and storage facilities
    - Reservoir facilities
    - Water treatment facilities
```

A PowerPoint file (Apache POI sample deck): `markdown` has a section per slide, notes included, and `slides[1]` is

```json
{
  "number": 2,
  "title": "This is the second slide",
  "hidden": false,
  "text": "- It has bullet points on it\n- They’re fun, aren’t they?\n- Especially in a different font like Arial Black at 16 point!",
  "notes": "These are the notes of the 2nd slide\nTHIS LINE IS BOLD"
}
```

An Excel file (IRS statistics): `markdown` is a table, `tables[0].rows` the same cells as arrays, `sheets` the facts about the sheet:

```json
{ "name": "Sheet1", "state": "visible", "rowCount": 17, "columnCount": 6, "rowsReturned": 17, "truncated": false }
```

```markdown
### Sheet: Sheet1

| Alabama, Fiscal Year 2023 |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- |
|  | Number of returns filed | Number of returns filed electronically | Internal Revenue gross collections (thousands of dollars) | ... |
| Total [1] | 3,471,529 | 2,690,994 | 35,829,649 | 1,706,757 | 6,809,200 |
| Estate tax | 391 | n/a | 309,381 | 27 | 12,315 |
```

### Input

| Field | Default | What it does |
|---|---|---|
| `urls` | – | Addresses of the files. Bad entries get a free `invalid` record, they never stop the run. |
| `outputFormats` | all four | `markdown`, `text`, `tables` (every table as JSON), `slides` (one object per slide). Choose fewer for lighter records. |
| `maxRowsPerSheet` | 1000 | Spreadsheets: non-empty rows converted per sheet (up to 100,000). The record tells you the total and that it was cut. The memory of the run can cut earlier on a very big sheet (see the FAQ). |
| `includeHiddenSheets` | off | Hidden sheets are skipped (and the warnings say so) unless you turn this on. |
| `includeSpeakerNotes` | on | Notes after each slide. |
| `maxFileMegabytes` | 50 | Bigger files are refused (free, `too_large`), the download is stopped as soon as the limit is passed. |
| `respectRobotsTxt` | on | The robots.txt of the site is read before the first request and followed for every redirect. |
| `requestsPerSecond` | 2 | Per host. A `Crawl-delay` in robots.txt can only slow it down. |
| `maxConcurrency` | 3 | Documents converted at the same time. |
| `timeoutSecs` | 60 | Download plus conversion, per document. |

### Output fields

One record per input address, in the **Overview** view; the same record has everything.

| Field | Meaning |
|---|---|
| `url`, `finalUrl` | The address as given and after redirects (credentials removed). |
| `status` | See the table below. |
| `error` | Why there is no (or only a partial) result, in one sentence; `null` when all went well. |
| `charged` | `true` only for `ok` and `partial`. |
| `httpStatus`, `fileName`, `sizeBytes`, `sha256` | About the download. `sha256` is the hash of the file: use it to detect changes. |
| `format` | `docx`, `pptx`, `xlsx`, `odt`, `odp` or `ods`, as found in the content. |
| `title`, `titleSource` | The title of the file properties; if there is none, or it is a program default ("Document1", "Microsoft Word - …"), the first heading (`first-heading`) or first slide title (`first-slide-title`). |
| `subject`, `keywords`, `language` | From the file properties; `language` also from the language of the text when the file does not say (BCP 47, e.g. `en-US`). |
| `createdAt`, `modifiedAt`, `application` | Dates (ISO 8601, UTC) and the program that made the file. |
| `pageCount` | Word and Writer only, **as declared by the file** (Word does not store the layout's pages, so the number may be stale); `null` otherwise. |
| `slideCount`, `sheetCount` | Slides; sheets (hidden ones included). |
| `tableCount`, `imageCount` | Tables converted (one per worksheet); pictures found (not converted). |
| `wordCount`, `charCount` | Of the converted text. |
| `hasMacros` | The file holds macros. They are never read or run. |
| `truncated`, `fullTextKey` | `true` when rows were cut or the text was too long for a record; then the whole Markdown is in the key-value store under `fullTextKey`. |
| `markdown`, `text` | The document as Markdown and as plain text. |
| `tables` | `[{ index, name, location, rowCount, columnCount, rows }]`, `rows` being arrays of cell texts. |
| `slides` | PPTX/ODP: `[{ number, title, hidden, text, markdown, notes }]`. |
| `sheets` | XLSX/ODS: `[{ name, state, rowCount, columnCount, rowsReturned, truncated }]`. |
| `warnings` | What the conversion could not carry over (see below). |
| `extractedAt` | When the record was made. |

#### Statuses

| `status` | Meaning | Charged |
|---|---|---|
| `ok` | Converted. | yes |
| `partial` | Converted, but a part could not be read (a damaged slide or sheet; the warnings name it). | yes |
| `empty` | A valid file with no text (only pictures, or blank). | no |
| `password_protected` | The file is encrypted. It cannot be read. | no |
| `unsupported_format` | Legacy `.doc` / `.xls` / `.ppt`, RTF, EPUB, XLSB, OpenDocument drawings… The message says what it is and to save it as DOCX / PPTX / XLSX. | no |
| `not_office` | The URL is not an Office document: an HTML page, a PDF, a ZIP, an empty answer. | no |
| `corrupt` | A damaged or truncated file. | no |
| `unsafe_archive` | Refused for safety: a zip bomb (absurd compression ratio, or parts larger than they declare). | no |
| `too_large` | Over `maxFileMegabytes`, or a document whose XML is too big to be read safely. | no |
| `blocked` | robots.txt forbids it, the site answered 401/403/429 or a bot-challenge page, or the address leads to a private network. Nothing is done to get around it. | no |
| `unreachable` | The download failed (DNS, timeout, 404, 5xx after one retry). | no |
| `invalid` | Not a usable web address. | no |

#### Warnings: what is not carried over

The conversion tells you, record by record, what it left out, so you do not have to guess:

- **Pictures** (counted in `imageCount`; the author's alt text is kept when it says something) – there is **no OCR** and no image description.
- **Charts** in Word and Excel (in PowerPoint a chart becomes a table of its values), **equations** (read as plain text), **SmartArt** (its text only), comments, headers and footers, the table of contents (the headings are there).
- **Formulas are not calculated**: the value Excel saved is shown; a formula without a saved value is an empty cell, and the warning counts them.
- **Macros** are flagged (`hasMacros`) and never read.
- Hidden sheets, rows cut at `maxRowsPerSheet` (or by the memory of the run on a very big sheet), columns beyond 500.

### Pricing

**One event: `document` – $0.0015** for each file converted (`ok` or `partial`). Nothing else is charged: no start fee, no per-page, per-slide or per-row price.

| You convert | Cost |
|---|---|
| 1 document | $0.0015 |
| 100 documents | $0.15 |
| 1,000 documents | $1.50 |
| 1,000 addresses where 150 are broken links or protected files | $1.275 (850 × $0.0015) |

Platform usage (compute) is included in that price: you pay nothing else.

**Spending limit:** if you set a maximum cost per run, the Actor stops cleanly when it is reached ("spending limit reached" in the run summary) and does not download more files.

### Safe and polite by design

- **Privacy:** the **author** and **last editor** fields of the file properties are read by nobody: they are not in the output (personal data).
- **robots.txt** is respected (checked for every site and every redirect), with an honest `User-Agent` that names the bot. No proxies, no disguise, no CAPTCHA tricks: a block page is reported as `blocked`.
- **Private networks are never reached**, not even through a redirect or a name that resolves to a private address.
- **Zip bombs** and absurd files are refused: the size is checked while the file is unpacked, not just as the header declares it.
- **Nothing is executed.** Macros, scripts, links and embedded objects are never run or followed.
- A restarted run does not deliver or charge the same document twice.

### FAQ

**Does it do OCR?** No. A scanned image inside a Word file stays an image (counted, with its alt text if any). For text in images use an OCR Actor.

**Can it read an old `.doc`, `.xls` or `.ppt`?** No, those are a different (binary) format. You get a free record saying so; save the file as DOCX / XLSX / PPTX once and run again.

**What about password-protected files?** They cannot be read; the record is `password_protected` and free.

**Why is `pageCount` null for my Word file?** Word stores the page count only when it saved the file with it. We report what the file declares and never invent a number. Slides and sheets are always counted.

**Are the numbers in my spreadsheet what Excel shows?** Dates, percentages, thousands separators, decimals and currency follow the cell format. Formulas are not recalculated. If a number looks different from what you see in Excel, tell us the format code.

**Is there a memory limit?** Yes, and a big file never crashes the run. Spreadsheets (XLSX, ODS) are read as a stream, so the size of the file is not the limit: the rows you get back are, because they are kept in memory until the record is written. With the default 1 GB that is about 40,000 rows of 10 short columns (numbers, dates) or about 18,000 rows of 17 text columns (a long public register is that kind of file). Past that the sheet is cut: the record says `truncated: true`, `sheets[].rowCount` has the real total, and a warning names the sheet ("Rows cut to stay within the memory of the run: give the run more memory or lower `maxRowsPerSheet`"). A cut file is still converted and charged like any other; the other files of the run are not affected. More memory gives proportionally more rows (4 GB about four times as many). Files refused for safety are free (`too_large`) with the reason in `error`: an XLSX or ODS sheet over 256 MB unpacked, an XLSX whose table of shared strings does not fit in a quarter of the run's heap (about 90 million characters of text on 1 GB), a Word, PowerPoint, Writer or Impress file whose XML is over 30 MB unpacked, or a package over 400 MB unpacked.

**My workbook is huge.** The first `maxRowsPerSheet` non-empty rows of each sheet are converted, and `sheets[]` has the real row counts. Raise the limit (up to 100,000) if you need more, and give the run more memory if the record says the rows were cut by memory; very long outputs are saved in the key-value store.

**Does it follow links inside the documents?** No. Links are kept as Markdown links; only the addresses you give are downloaded.

**Is a file I put on a private server readable?** Only public `http(s)` addresses. Private and local addresses are refused by design, and credentials in a URL are removed and not used.

**How does it compare with Microsoft's MarkItDown?** MarkItDown is an excellent open-source library; this Actor is the hosted version for batches: URLs in, records out, with the safety limits, the per-file status, the tables as JSON, the notes per slide and the "what was lost" warnings built in.

### Integration

Run it from the Apify API, the CLI, or your own code.

**JavaScript**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('far003/office-docs-markdown').call({
    urls: ['https://example.org/handbook.docx', 'https://example.org/q3-results.pptx'],
    outputFormats: ['markdown', 'tables'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const doc of items) {
    if (doc.status === 'ok') console.log(doc.title, doc.wordCount, doc.markdown.slice(0, 200));
    else console.warn(doc.url, doc.status, doc.error);
}
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("far003/office-docs-markdown").call(run_input={
    "urls": ["https://example.org/handbook.docx"],
    "maxRowsPerSheet": 5000,
})
for doc in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(doc["status"], doc["format"], doc["title"], doc["wordCount"])
```

**Chunking for RAG:** split `markdown` on `\n## ` for slides and sheets, on headings for Word files; `slides[].markdown` and `tables[]` are already cut at natural boundaries.

Run summary (totals per status, charged, skipped) is stored under the key `SUMMARY` of the default key-value store.

### Related

- [PDF to Text & Markdown Extractor](https://apify.com/far003/pdf-to-text-markdown-extractor) – the same job for PDFs: reading order, columns, headings, tables, bookmarks.

# Actor input Schema

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

Addresses (http or https) of Word, PowerPoint or Excel files (DOCX, PPTX, XLSX) or OpenDocument files (ODT, ODP, ODS). One record per address. The type is recognised from the file itself, not from the extension. Addresses that are not usable web addresses, or that point to private/local networks, get an "invalid" record (free), not a run error. Credentials written inside a URL are removed and never used.

## `outputFormats` (type: `array`):

"markdown" and "text" are the document body; "tables" adds every table as JSON (rows and cells); "slides" adds one object per slide with its notes (PPTX/ODP). Choose fewer to keep the records small. All four by default.

## `maxRowsPerSheet` (type: `integer`):

Spreadsheets (XLSX, ODS): at most this many non-empty rows of each sheet are converted; the record says how many rows the sheet has and that it was cut (truncated, sheets\[]). Empty rows and empty leading columns are dropped. Up to 500 columns are read. On a very big sheet the memory of the run can cut earlier; the record says so.

## `includeHiddenSheets` (type: `boolean`):

By default hidden sheets are skipped (and the warnings say so). Turn on to convert them too.

## `includeSpeakerNotes` (type: `boolean`):

Presentations (PPTX, ODP): add the speaker notes of each slide to the Markdown, and to slides\[].notes.

## `maxFileMegabytes` (type: `integer`):

Larger files are not read (free, status too_large). The download is stopped as soon as the limit is passed. A document is also refused, for safety, when it unpacks to far more than it should (zip bombs).

## `respectRobotsTxt` (type: `boolean`):

Check the robots.txt of the site every document (and every redirect) comes from. Documents it forbids are not downloaded (free, status blocked).

## `requestsPerSecond` (type: `integer`):

Politeness limit for each host. A Crawl-delay in robots.txt can only slow it down.

## `maxConcurrency` (type: `integer`):

Each document is held in memory while it is converted. With large files, lower this or raise the memory of the run.

## `timeoutSecs` (type: `integer`):

Download plus conversion. Also the longest silence allowed while a file arrives.

## Actor input object example

```json
{
  "urls": [
    "https://www.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
    "https://raw.githubusercontent.com/apache/poi/trunk/test-data/slideshow/SampleShow.pptx",
    "https://www.irs.gov/pub/irs-soi/23dbalabama.xlsx"
  ],
  "outputFormats": [
    "markdown",
    "text",
    "tables",
    "slides"
  ],
  "maxRowsPerSheet": 1000,
  "includeHiddenSheets": false,
  "includeSpeakerNotes": true,
  "maxFileMegabytes": 50,
  "respectRobotsTxt": true,
  "requestsPerSecond": 2,
  "maxConcurrency": 3,
  "timeoutSecs": 60
}
```

# Actor output Schema

## `documents` (type: `string`):

No description

## `content` (type: `string`):

No description

## `tables` (type: `string`):

No description

## `slides` (type: `string`):

No description

## `problems` (type: `string`):

No description

## `summary` (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://www.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
        "https://raw.githubusercontent.com/apache/poi/trunk/test-data/slideshow/SampleShow.pptx",
        "https://www.irs.gov/pub/irs-soi/23dbalabama.xlsx"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("far003/office-docs-markdown").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.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
        "https://raw.githubusercontent.com/apache/poi/trunk/test-data/slideshow/SampleShow.pptx",
        "https://www.irs.gov/pub/irs-soi/23dbalabama.xlsx",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("far003/office-docs-markdown").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://www.epa.gov/system/files/documents/2025-05/drinking-water-erp-template.docx",
    "https://raw.githubusercontent.com/apache/poi/trunk/test-data/slideshow/SampleShow.pptx",
    "https://www.irs.gov/pub/irs-soi/23dbalabama.xlsx"
  ]
}' |
apify call far003/office-docs-markdown --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,far003/office-docs-markdown"
        }
    }
}
```

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/j5falEmQs3hGvsoz5/builds/nTTlPK5DFl2mC4dzx/openapi.json
