LLM JSON Repair & Schema Validator
Pricing
$0.50 / 1,000 repaired jsons
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
Maintained by CommunityActor 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
| Parameter | Type | Default | Meaning |
|---|---|---|---|
texts | array of strings | required | 1–100 texts, at most 1,000,000 characters each and 5,000,000 characters combined |
schema | object | omitted | Optional JSON Schema, at most 100,000 serialized characters; local fragment references only |
extractMode | first or all | first | Select first structural block or combine all selected blocks |
failOnError | boolean | false | Fail 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 size | Object with long string, median | Many small objects needing syntax repair, median |
|---|---|---|
| 10 KB (10,000 bytes) | 0.662 ms | 6.944 ms |
| 100 KB (100,000 bytes) | 6.527 ms | 68.586 ms |
| 1 MB (1,000,000 bytes) | 64.818 ms | 681.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.txtpython -m pytest actors/llm-json-repair/tests -qpython 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.