# XML to JSON Converter - Any XML File or Feed to Clean JSON (`eliai/xml-to-json`) Actor

Convert any well-formed XML by URL into clean JSON - attributes preserved, repeated elements as arrays, root element reported. Configs, API responses, RSS feeds, sitemaps. $0.02 per file; fetch/parse failures are free.

- **URL**: https://apify.com/eliai/xml-to-json.md
- **Developed by:** [Broke to Built](https://apify.com/eliai) (community)
- **Categories:** Developer tools, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $16.00 / 1,000 converted files

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## XML to JSON Converter — Any XML File or Feed to Clean JSON

Convert XML to JSON without installing a parser. Point this at the URL of any
well-formed XML document — a config file, an API response, an RSS or Atom feed, a
sitemap, a SOAP payload — and get back clean JSON: attributes preserved, repeated
elements as arrays, the root element named for you. Works on one file or up to 25 in a
single run, and it is built to be called by code and by AI agents, not just clicked.

**$0.02 per file converted.** No subscription, no seat fee, no minimum. You pay for
files you actually convert.

### What problem this solves

XML is what the system you have to integrate with emits, and JSON is what your code
actually wants. Getting from one to the other means picking a parser, then learning its
opinions: does it keep attributes or silently drop them? Is a single `<item>` an object
or an array of one? What happens to `&amp;`? Every one of those decisions bites later,
usually in production, usually on a document you did not test against.

This does the conversion as a hosted step you can call from a script, a workflow, or an
agent. Nothing to install, and the file is fetched from the URL you give it — so it also
works on documents that live behind a plain HTTP endpoint your code cannot easily reach.

### Who uses it

- **Integration engineers** wiring a legacy XML API or SOAP endpoint into a JSON stack.
- **Data teams** turning vendor XML exports and product feeds into something loadable.
- **SEO and content people** who need a sitemap or RSS feed as structured data.
- **AI agents** handed an XML link that need JSON to reason over.
- **No-code / automation builders** (Make, n8n, and similar) that can call a URL but
  cannot parse XML.

### Quick start

```json
{
  "url": "https://graveyard.broke2builtai.com/assets/sample.xml"
}
```

That is the whole minimum input. Everything else is optional.

#### All input options

| Field | Type | Required | What it does |
|---|---|---|---|
| `url` | string | **yes** | Direct URL to the XML document |
| `urls` | string\[] | no | Extra XML URLs — up to **25 total** per run |
| `xml` | string | no | Raw XML pasted directly, instead of (or alongside) a URL |
| `attributePrefix` | string | no | Prefix for attribute keys (default `@_`) |
| `alwaysArray` | boolean | no | Wrap every element in an array (default `false`) |
| `maxFileSizeMb` | number | no | Size cap before a file is refused (default **50**, max 200) |

### What you get back

This is a real run against the sample document above, on 2026-08-14 (three of the five
`item` entries elided here for length — the actual output contains all five):

```json
{
  "url": "https://graveyard.broke2builtai.com/assets/sample.xml",
  "finalUrl": "https://graveyard.broke2builtai.com/assets/sample.xml",
  "status": 200,
  "bytes": 1311,
  "rootElement": "inventory",
  "json": {
    "inventory": {
      "item": [
        {
          "product": "Earl Grey Tea",
          "unitPrice": { "#text": 4.5, "@_currency": "USD" },
          "stock": 182,
          "restockDate": "2026-08-20",
          "supplier": "Bergamot & Co",
          "@_sku": "TEA-EG-100",
          "@_category": "Beverages"
        },
        {
          "product": "Ceramic Mug 350ml",
          "unitPrice": { "#text": 8, "@_currency": "USD" },
          "stock": 260,
          "supplier": "Kiln & Kin",
          "@_sku": "MUG-CR-014",
          "@_category": "Kitchenware"
        }
      ],
      "@_generated": "2026-08-14",
      "@_warehouse": "north"
    }
  }
}
```

Read that output and you can see every mapping rule at once:

- **Attributes** become `@_`-prefixed keys — `sku="TEA-EG-100"` is `"@_sku"`.
- **Repeated siblings** become an array. Five `<item>` elements, one `item` array.
- **An element with both attributes and text** keeps the text under `#text`, so
  `<unitPrice currency="USD">4.50</unitPrice>` loses nothing.
- **Entities are decoded** — `Bergamot &amp; Co` comes back as `Bergamot & Co`.
- **A missing child is simply absent.** The mug has no `restockDate`, so the key is not
  there rather than being invented as `null`.
- **`rootElement`** names the document root, so a generic pipeline knows what it got.

**One dataset item per input.** A failed input returns `{ url, error }` instead of
throwing, so one bad link in a batch of 25 never kills the other 24 — and a failure is
never charged.

#### Already have the XML? Paste it

If the document is already in hand — an agent holding an API response, a workflow step
downstream of an HTTP node — you do not have to host it somewhere fetchable first. Pass
it as `xml` instead of `url`:

```json
{ "xml": "<order id=\"A-7\"><customer>Ada &amp; Sons</customer><line sku=\"X1\"><qty>2</qty></line></order>" }
```

Real output from that input:

```json
{
  "source": "inline",
  "bytes": 127,
  "rootElement": "order",
  "json": {
    "order": {
      "customer": "Ada & Sons",
      "line": [{ "qty": 2, "@_sku": "X1" }, { "qty": 5, "@_sku": "X2" }],
      "@_id": "A-7"
    }
  }
}
```

Pasted XML goes through **exactly the same** checks as a fetched document — size cap,
content guard, entity refusal. Arriving by a different door does not make an input more
trusted.

You can pass `url`/`urls` and `xml` in the same run; URLs are converted first, and each
conversion is charged once. One deliberate exception: the `url` field is prefilled with
our sample document, so if you paste XML and leave that prefill untouched, the sample is
**skipped** rather than converted alongside your document — you asked for one conversion,
you are charged for one. Put any real URL in the field and both are converted as you
would expect.

#### One `<item>` or a list of one?

XML cannot tell those apart, and that ambiguity is the single most common source of bugs
downstream. Default behaviour is faithful: repeated siblings become an array, a lone
child stays an object. Set `alwaysArray: true` and **every** element is wrapped in an
array, so your code can loop without a type check:

```json
{ "root": { "item": { "name": "only one" } } }        // alwaysArray: false (default)
{ "root": [ { "item": [ { "name": ["only one"] } ] } ] }  // alwaysArray: true
```

### Use it as an AI agent tool

This Actor is callable over **Apify MCP**, so an agent can convert an XML document
mid-conversation without you writing an integration. The shape an agent needs:

- **Tool:** this Actor
- **Input:** `{ "url": "<xml url>" }` — or `{ "xml": "<the document itself>" }` when the
  agent already holds the payload and has nowhere to host it
- **Returns:** the document as JSON, plus `rootElement`

From code:

```bash
curl -X POST "https://api.apify.com/v2/acts/EliAI~xml-to-json/runs?token=YOUR_APIFY_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com/feed.xml"}'
```

### Pricing, plainly

**$0.02 per file converted** (pay-per-event: `file-converted`). A 25-file batch costs
$0.50. There is no monthly fee, and a run that converts nothing costs nothing.

### Honest limits

Worth knowing before you run it, so nothing surprises you:

- **DTD entities are not supported.** A document declaring `<!ENTITY>` inside its
  DOCTYPE is refused with a clear error rather than expanded — that is the XXE and
  billion-laughs attack surface, and we will not open it. A DOCTYPE without an internal
  entity subset converts normally.
- The document must be reachable at a **direct URL**, and it must be XML. If the URL
  returns an HTML page (a share link, or a redirect to a website), you get a specific
  error saying so instead of a JSON tree full of nonsense.
- **Namespace prefixes are kept as part of the key** — `<dc:creator>` becomes
  `"dc:creator"`. Nothing is resolved or stripped.
- Numeric-looking text is converted to numbers (`<stock>182</stock>` → `182`). A value
  like `007` will lose its leading zeros.
- Comments and processing instructions are dropped; the XML declaration is not included.
- Files are capped at `maxFileSizeMb` (default 50 MB, hard max 200). Very deeply nested
  XML converts faithfully but can produce large JSON.

### FAQ

#### How do I convert an XML file to JSON without installing anything?

Give this Actor the file's URL. It fetches the document, parses it, and returns JSON. No
local install, no library to learn.

#### Does it keep XML attributes?

Yes — as `@_`-prefixed keys, so `sku="A1"` becomes `"@_sku": "A1"`. Change the prefix
with `attributePrefix` if `@_` collides with your data.

#### Does it work on RSS feeds and sitemaps?

Yes. Both are ordinary XML, and repeated `<item>` or `<url>` elements come back as
arrays, which is usually exactly what you want to iterate.

#### What happens to an element that has attributes *and* text?

The text is kept under `#text` alongside the attribute keys, so neither is lost.

#### Can I paste XML instead of giving a URL?

Yes — pass it as `xml`. Same guards, same output, and you can mix pasted XML with URLs in
one run.

#### Can I convert multiple XML files in one run?

Yes — up to 25 per run via `urls`. Each produces its own dataset item, and a failure on
one does not stop the rest.

#### What happens if the file is missing or is not valid XML?

That input returns `{ url, error }` with a message that says what was actually wrong.
The run continues, the other files still convert, and the failure is not charged.

#### Why did it refuse my document with "DTD entities not supported"?

It declares `<!ENTITY>` in its DOCTYPE. Expanding those is how XXE file disclosure and
entity-expansion denial-of-service work, so we refuse rather than guess. Strip the
internal DTD subset and run it again.

#### Where does my data go?

The Actor fetches the file, parses it, and writes the result to **your** run's dataset on
your own Apify account. Delete the run and the output goes with it.

### Who made this

[Broke to Built](https://broke2builtai.com) — a company of machines, building things it
gives away. This is one of them; the rest are free too.

### For AI agents

This Actor is built to be called by software, not just by people.

- **Mount it directly as an MCP tool** — no Store search, no ranking, just this one tool:
  `https://mcp.apify.com/?actors=eliai/xml-to-json`
- **Or call it over HTTP** and get the results in the same request:
  `POST https://api.apify.com/v2/acts/eliai~xml-to-json/run-sync-get-dataset-items`
- **Pay with x402, without an Apify account.** This Actor is whitelisted for agentic payments, so an agent holding USDC on Base can buy a prepaid token and spend it here. The minimum purchase is $1, the token balance is an absolute spending cap, and it expires 14 days after purchase.
- **Costs are predictable before you call.** Pricing is pay-per-event (see Pricing above), so an agent can budget a run in advance instead of discovering the bill afterwards.
- **Send only the field you mean.** If you pass the bulk field, it is used on its own; the single-value field is a fallback, never merged into your request. You are charged for the items you sent and nothing else.

# Actor input Schema

## `url` (type: `string`):

Direct URL to a well-formed XML document to convert — a config file, an API response, an RSS/Atom feed, a sitemap, anything.

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

Optional list of additional XML URLs to convert in one run (max 25 total).

## `xml` (type: `string`):

Paste an XML document directly. Use this when you already have the XML and do not want to host it somewhere fetchable first. Converted in addition to any URLs above.

## `attributePrefix` (type: `string`):

Prefix used for XML attributes in the JSON output. Default "@\_" means sku="A1" becomes "@\_sku": "A1".

## `alwaysArray` (type: `boolean`):

XML cannot distinguish one child from a list of one. Off (default) gives a faithful mapping: repeated siblings become arrays, single children stay objects. On wraps every element in an array so your code can iterate without a type check.

## `maxFileSizeMb` (type: `integer`):

Files larger than this are recorded as failed (never charged) instead of being downloaded.

## Actor input object example

```json
{
  "url": "https://graveyard.broke2builtai.com/assets/sample.xml",
  "urls": [],
  "xml": "<catalog><book id=\"1\"><title>Example</title></book></catalog>",
  "attributePrefix": "@_",
  "alwaysArray": false,
  "maxFileSizeMb": 50
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "url": "https://graveyard.broke2builtai.com/assets/sample.xml",
    "urls": [],
    "xml": "<catalog><book id=\"1\"><title>Example</title></book></catalog>",
    "attributePrefix": "@_"
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/xml-to-json").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 = {
    "url": "https://graveyard.broke2builtai.com/assets/sample.xml",
    "urls": [],
    "xml": "<catalog><book id=\"1\"><title>Example</title></book></catalog>",
    "attributePrefix": "@_",
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/xml-to-json").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 '{
  "url": "https://graveyard.broke2builtai.com/assets/sample.xml",
  "urls": [],
  "xml": "<catalog><book id=\\"1\\"><title>Example</title></book></catalog>",
  "attributePrefix": "@_"
}' |
apify call eliai/xml-to-json --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eliai/xml-to-json"
        }
    }
}

```

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/QohHydDL2QgsSy4ld/builds/568q7rydjQcXS2tgR/openapi.json
