LLM JSON Repair & Schema Validator avatar

LLM JSON Repair & Schema Validator

Pricing

$0.50 / 1,000 repaired jsons

Go to Apify Store
LLM JSON Repair & Schema Validator

LLM JSON Repair & Schema Validator

Fast, deterministic repair and schema validation for broken or malformed JSON from ChatGPT, Claude, DeepSeek, and local LLMs.

Pricing

$0.50 / 1,000 repaired jsons

Rating

0.0

(0)

Developer

Kadev Code

Kadev Code

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Repair structural JSON from LLM output and validate it against an optional JSON Schema. Parsing runs locally without calling an LLM, downloading schemas, or executing input code. The engine accepts a conservative subset of syntax errors so that ambiguous content does not become a paid result.

Supported repairs include single-quoted strings, unquoted object keys, Python True/False/None, trailing commas, comments outside strings, Markdown fences, and missing closing brackets after complete values. For example, {name: 'Alice', active: True,} becomes {"name":"Alice","active":true}. An escaped apostrophe in {'msg': 'it\'s ok'} is preserved as "it's ok". An unescaped apostrophe, incomplete string, missing value, duplicate key, non-finite number, or unrecognized bare value is rejected. Floating-point numbers whose decimal value cannot survive output serialization are also rejected; integers remain Python integers. For example, {"x": 0.1234567890123456789} is rejected rather than rounded. A literal newline inside a quoted string is also rejected because guessing its escape could change the value; submit the JSON escape \n instead. Missing closing brackets after complete values can be repaired, but extra closing braces/brackets such as {"a":1}}} produce a warning, lossy=true, and no result.

Input

ParameterTypeDefaultMeaning
textsarray of stringsrequired1–100 texts, at most 1,000,000 characters each and 5,000,000 characters combined
schemaobjectomittedOptional JSON Schema, at most 100,000 serialized characters; local fragment references only
extractModefirst or allfirstSelect first structural block or combine all selected blocks
failOnErrorbooleanfalseFail the run after delivering the first invalid result

The API also accepts a singular string text when texts is absent. Explicit texts: [], missing input and incorrect types produce a clear input error and no custom charge. Examples are provided as Console prefill only; missing input never starts a paid demonstration. Empty individual strings produce invalid results. JSON nesting is limited to 100 levels.

This example uses two records that both satisfy the supplied schema:

{
"texts": [
"Here is the result: {name: 'Alice', active: True,}",
"{\"name\":\"Bob\",\"active\":false}"
],
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"active": {"type": "boolean"}
},
"required": ["name", "active"]
},
"extractMode": "first",
"failOnError": false
}

Selection and preservation

Only object or array roots are accepted. Scalar roots and empty containers have valid=false. Placeholders such as {name} are ignored because they contain no key/value pair. Arrays containing only numbers, only strings, or only booleans embedded in unfenced prose are treated as ambiguous citations, options, or flags. Examples include See section [3] for details, Choose from ['red', 'blue'], and flags [true, false]; none produces a valid result. Submit such arrays directly ([3], [true, false]) or in a Markdown code fence to make their intent explicit. This conservative heuristic can also reject adjacent scalar arrays without prose. Mixed arrays and object arrays still follow structural extraction.

first selects the first structural candidate and preserves an array root. Later blocks and surrounding prose are intentionally outside that selection. all appends object blocks and flattens array blocks one level; for a schema with type: object, each resulting element is validated separately. For other schemas the combined array is validated. This selection is part of the input contract, rather than an attempt to preserve every word of surrounding prose. Result: {"ok": true} ... Thanks {for} reading in all mode yields [{"ok": true}], with a warning for the ignored placeholder.

Every token inside each selected block must be accounted for by a supported syntax conversion. The engine does not salvage a smaller object by dropping unrecognized fields. For {"a": 1, bla bla bla}, it returns valid=false, lossy=true, and a warning. No partial repaired data is returned or charged. Here lossy=true means content preservation could not be verified and the repair was refused; it never denotes a successful paid repair. Closing JSON brackets outside candidate blocks are conservatively considered unmatched, even after the first selected block; they are never silently discarded.

Output and billing

An ordinary completed run produces one dataset row per submitted text. Invalid batch configuration instead produces one error row with index: null and fails the run. Spending limits, failOnError, or storage failures can stop processing before all rows are delivered.

{
"index": 0,
"valid": true,
"repaired": {"name": "Alice", "active": true},
"issuesDetected": [
"quoted_unquoted_keys",
"converted_single_quotes",
"normalized_python_constants",
"removed_trailing_commas",
"extracted_json_from_surrounding_text"
],
"schemaErrors": [],
"lossy": false,
"warnings": [],
"error": null
}

issuesDetected replaces the former heuristic repairsApplied field. It reports syntax categories observed during normalization, without offsets, counts or a complete edit history. warnings explains ambiguous repairs or extraction decisions; schemaErrors contains validation diagnostics. A schema mismatch can include the repaired data for inspection while keeping valid=false. Repeated warnings are aggregated into one string per category, with a suffix such as (250000 occurrences); a single occurrence keeps its original message. Each result is capped at 96,000 serialized ASCII-escaped JSON bytes, leaving space for the row index below a local 100 KB item budget. This includes repaired data and diagnostics. An oversized result becomes a small invalid error row with repaired=null, lossy=true, and no custom charge; data is never truncated into a valid result. This local cap is conservative and is not a claim about the platform's current item limit. Split large documents into smaller texts.

Proposed price: $0.0005 per valid delivered text (json-repaired), including already-valid input. A text with multiple selected blocks still costs one event. Invalid, empty, lossy or schema-failing results have no custom event charge. The SDK's push_data(..., charged_event_name="json-repaired") reserves budget, delivers an eligible row and charges it. The run stops at the reported limit. A delivery or charging exception fails the run without retry; earlier delivered results may already have been charged, and uncertain transport outcomes cannot be promised a refund by this code.

Before publication, remove both synthetic events apify-actor-start and apify-default-dataset-item in Apify Console so that neither errors nor starts generate automatic fees. The runtime refuses a paid synthetic configuration before pushing rows, but cannot undo a start fee already imposed by the platform. Console pricing has not been configured or tested in this local worktree. See .actor/pricing.md for fixed startup cost and limitations.

Performance and development

Measured locally on Windows, Python 3.12.13, 2026-10-22: one warmup followed by five timed repetitions of repair_single_text, without a schema, SDK initialization, dataset I/O or charging. Sizes below are decimal KB/MB and ASCII bytes equal character counts. These are observations, not Apify latency guarantees.

Input sizeObject with long string, medianMany small objects needing syntax repair, median
10 KB (10,000 bytes)0.662 ms6.944 ms
100 KB (100,000 bytes)6.527 ms68.586 ms
1 MB (1,000,000 bytes)64.818 ms681.792 ms

Both 10 KB workloads produce valid results. The 100 KB and 1 MB workloads exceed the output cap and produce small, uncharged error rows after parsing. Candidate scanning uses non-overlapping ranges and precomputed context indices, so surrounding-text checks do not rescan the input for every candidate.

In all mode, 100,000 repetitions of {"a":1} (800 KB) took a median 748.494 ms and returned a 243-byte oversized-result error row. Repeated [1] at exactly 1 MB took a median 1,345.985 ms and returned a 256-byte row with one aggregated warning. All five samples for both cases stayed below 2 seconds. See benchmark-results.json.

Reproduce from the repository root:

python -m pip install -r actors/llm-json-repair/requirements-dev.txt
python -m pytest actors/llm-json-repair/tests -q
python actors/llm-json-repair/benchmark.py

Add --baseline-ref e5da053 --output actors/llm-json-repair/benchmark-results.json to also measure the pre-R2 implementation once, loaded in memory using read-only git show, and save a comparison report. Baseline measurements can be slow.

The Docker image uses Python 3.12. Runtime dependencies exclude pytest and the permissive json-repair library; normalization uses Python's strict JSON decoder and explicit token transformations. Tests block external connections and place SDK storage under pytest tmp_path using APIFY_LOCAL_STORAGE_DIR. The actor's .gitignore covers local storage/ and Python artifacts. Default and minimum memory are 256 MB, with a 512 MB maximum; cloud peak memory is not yet measured.

Errors, retention and responsible use

Unsupported or remote schema references return an explicit error without fetching the reference. Only local # fragments are supported, including $defs links. Do not submit secrets or personal data that you are not authorized to process. Review repaired output before using it for consequential actions: structural validity does not prove that an LLM's statements are correct.

The Actor creates no extra raw-input files and uses no third-party model API. Apify retains the run input and dataset according to its storage settings; data is not automatically discarded when the run completes. Manage retention and deletion in your own Apify account. Local test storage is temporary.

References: SDK event charging, platform monetization.