# spruce_the_data: messy HTML and text to clean JSON (`sprucethedata/clean-json`) Actor

Turns messy scraped text or HTML into clean JSON that matches your schema.

- **URL**: https://apify.com/sprucethedata/clean-json.md
- **Developed by:** [Spruce Data](https://apify.com/sprucethedata) (community)
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 cleaned items

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

## spruce_the_data: messy HTML and text to clean JSON

Scraped pages are messy: leftover HTML, menus, cookie banners, prices written five different
ways. **spruce_the_data** reads that mess and returns tidy JSON in exactly the shape you ask for.
You describe the fields you want with a JSON Schema, and every result matches it.

- **Your schema, your fields.** Products, job ads, events, contacts, listings: anything you
  can describe as a JSON object.
- **Give it links or text.** Paste web page URLs and it downloads and cleans each page, or
  send text and HTML you already have.
- **Works with any scraper.** Point it at another Actor's dataset and it cleans every item.
- **Validated output.** Results are checked against your schema. Emails, dates, URLs and
  phone numbers get a format check too; a nullable field that still fails comes back as
  `null` and is listed in `droppedFields`, so you know which values to double-check.
- **Failed items are free.** You only pay for items that come back clean.
- **Built-in prompt-injection defenses.** Instructions hidden in a page ("ignore previous
  instructions...") are treated as data, which makes them hard to use against you.

### Quick start

1. Paste one or more messy texts or HTML snippets into **Texts to clean**, or web
   addresses into **Web page URLs**.
2. Describe the shape you want in **Output JSON Schema** (or leave it empty for a general
   title, summary and entities extraction).
3. Click **Start**. Results appear in the **Output** tab, one row per input.

#### Example input

```json
{
  "texts": [
    "<h1> Stellar Gooseneck Kettle 1.0L </h1><span class=\"price\">$49.99</span> In stock"
  ],
  "outputSchema": {
    "type": "object",
    "properties": {
      "name": { "type": "string" },
      "price": { "type": ["number", "null"] },
      "currency": { "type": ["string", "null"], "description": "ISO 4217 code, e.g. USD" },
      "in_stock": { "type": ["boolean", "null"] }
    },
    "required": ["name", "price", "currency", "in_stock"]
  }
}
```

#### Example output

```json
{
  "index": 0,
  "status": "success",
  "data": {
    "name": "Stellar Gooseneck Kettle 1.0L",
    "price": 49.99,
    "currency": "USD",
    "in_stock": true
  },
  "droppedFields": []
}
```

- `index` is the position of the item in your input, so you can match results back.
- `droppedFields` lists any fields that failed a format check and were set to `null`.
- A failed item looks like `{"index": 3, "status": "failed", "error": "..."}` and is not
  charged.

### Cleaning web pages by URL

Put one address per line in **Web page URLs**:

```json
{
  "urls": ["https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"],
  "outputSchema": {
    "type": "object",
    "properties": {
      "title": { "type": "string" },
      "price": { "type": ["number", "null"] },
      "currency": { "type": ["string", "null"], "description": "ISO 4217 code, e.g. GBP" },
      "in_stock": { "type": ["boolean", "null"] }
    },
    "required": ["title", "price", "currency", "in_stock"]
  }
}
```

Each result row also has a `url` field, so you can match it to the page.

- **Best for single item pages:** one product, job ad, listing or profile per URL.
- **Plain downloads only.** Pages that need JavaScript to show their content, need a
  login, or block automated visitors won't load. Those items fail with a message and are
  free; for such sites, run a full scraper first and clean its dataset (below).
- Pages up to 2 MB, 45 seconds to load, public `http` and `https` addresses only.

### Cleaning another Actor's results

Already have a scraper? Run it first, then:

1. Copy its **dataset ID** (on the run's Storage tab).
2. Paste it into **Source dataset ID** here.
3. Set **Text field in the source dataset** to the field that holds the page text or HTML
   (for example `text`, `html` or `description`).

Every item in that dataset is cleaned into your schema, up to **Max items**.

### Tips for accurate results

- **Add a `description` to fields.** "ISO 4217 code, e.g. USD" or "price in the listing's
  own currency, as a number" makes a real difference.
- **Use nullable types** (`["number", "null"]`) for anything that might be missing, so the
  model can say "not there" instead of guessing.
- **Send raw HTML if you have it.** Scripts, styles and layout are stripped automatically,
  and useful attributes (like a `star-rating` class) are kept as hints.
- **One record per item works best.** Long list pages with dozens of entries work, but are
  slower and count as large items.

### Pricing

You pay per successfully cleaned item. Failed items are always free.

- **Item cleaned:** charged once per item that comes back clean.
- **Large item (extra):** added when an item's output is large (over about 600 tokens),
  such as a long list page, because those take several times longer to process.
- **Run start:** a tiny fee per run, set by Apify.

The current prices are shown on this Actor's **Pricing** tab. You can cap the cost of any
run with Apify's **maximum cost per run** setting; the Actor stops cleanly when the cap is
reached.

Users on Apify's free plan can clean **5 items per run** to try it out.

### Limits and speed

- About **5 seconds** per typical item; long list pages can take 30 to 50 seconds.
- Items are processed one at a time, so large batches take a while. Start them in the
  background and come back.
- Each input can be up to 300,000 characters of raw HTML or text. After cleanup, the part
  the model reads is capped at 20,000 characters; longer items fail (free) with a message.
- Ratings shown only as star images with no text or attributes can't be read.
- Link addresses (`href`) and image URLs inside a page aren't read, only the visible text.
  If you need one, include it in your text or take it from your scraper's own output.
- The service is occasionally unavailable for maintenance. Busy or unavailable moments are
  retried automatically; anything that still fails is not charged.

### Privacy

Your texts are used only to produce your results. The cleaning service doesn't store or
log them and doesn't use them to train models. Your inputs and results stay in your own
Apify storage, under your account's retention settings.

### Questions or problems?

Open an issue on this Actor's **Issues** tab with an example input (remove anything
private) and what you expected to get back.

# Actor input Schema

## `texts` (type: `array`):

Messy text or HTML snippets, one per item. Leave empty if you use URLs or a source dataset instead.

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

Web pages to download and clean, one address per line. Works best on single item pages (a product, job ad or listing). Pages that need JavaScript to show their content, or that block automated visitors, won't load; those items fail and are free.

## `datasetId` (type: `string`):

Clean every item in another Actor's dataset, e.g. your scraper's results.

## `textField` (type: `string`):

Which field of each dataset item holds the messy text or HTML.

## `outputSchema` (type: `object`):

The shape you want back, as a JSON Schema with "type": "object". Field descriptions improve accuracy. Leave empty for a generic title/summary/entities extraction.

## `maxItems` (type: `integer`):

Stop after this many items.

## Actor input object example

```json
{
  "texts": [
    "<h1> Stellar Gooseneck Kettle 1.0L </h1><span class=\"price\">$49.99</span> In stock"
  ],
  "textField": "text",
  "outputSchema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string"
      },
      "price": {
        "type": [
          "number",
          "null"
        ]
      },
      "currency": {
        "type": [
          "string",
          "null"
        ],
        "description": "ISO 4217 code, e.g. USD"
      },
      "in_stock": {
        "type": [
          "boolean",
          "null"
        ]
      }
    },
    "required": [
      "name",
      "price",
      "currency",
      "in_stock"
    ]
  },
  "maxItems": 1000
}
```

# Actor output Schema

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

One row per input: the cleaned JSON on success, or an error message on failure.

# 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 = {
    "texts": [
        "<h1> Stellar Gooseneck Kettle 1.0L </h1><span class=\"price\">$49.99</span> In stock"
    ],
    "outputSchema": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string"
            },
            "price": {
                "type": [
                    "number",
                    "null"
                ]
            },
            "currency": {
                "type": [
                    "string",
                    "null"
                ],
                "description": "ISO 4217 code, e.g. USD"
            },
            "in_stock": {
                "type": [
                    "boolean",
                    "null"
                ]
            }
        },
        "required": [
            "name",
            "price",
            "currency",
            "in_stock"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("sprucethedata/clean-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 = {
    "texts": ["<h1> Stellar Gooseneck Kettle 1.0L </h1><span class=\"price\">$49.99</span> In stock"],
    "outputSchema": {
        "type": "object",
        "properties": {
            "name": { "type": "string" },
            "price": { "type": [
                    "number",
                    "null",
                ] },
            "currency": {
                "type": [
                    "string",
                    "null",
                ],
                "description": "ISO 4217 code, e.g. USD",
            },
            "in_stock": { "type": [
                    "boolean",
                    "null",
                ] },
        },
        "required": [
            "name",
            "price",
            "currency",
            "in_stock",
        ],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("sprucethedata/clean-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 '{
  "texts": [
    "<h1> Stellar Gooseneck Kettle 1.0L </h1><span class=\\"price\\">$49.99</span> In stock"
  ],
  "outputSchema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string"
      },
      "price": {
        "type": [
          "number",
          "null"
        ]
      },
      "currency": {
        "type": [
          "string",
          "null"
        ],
        "description": "ISO 4217 code, e.g. USD"
      },
      "in_stock": {
        "type": [
          "boolean",
          "null"
        ]
      }
    },
    "required": [
      "name",
      "price",
      "currency",
      "in_stock"
    ]
  }
}' |
apify call sprucethedata/clean-json --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sprucethedata/clean-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/UVnafxQ1H0j0tWgfD/builds/PD9NO6DlNcXgO8nsG/openapi.json
