To get JSON from an AI model without explanations, Markdown, or surrounding text, use three layers of control: define an unambiguous response contract, enable the API's structured output mode if available, and validate the result with a JSON parser together with schema or structural validation. A prompt alone reduces the chance of an error, but it does not guarantee that the response will be suitable for automated processing.
What the result should look like
If the application expects a single JSON object, the model's entire response should consist only of that object:
{
"title": "Example",
"priority": 2,
"published": true
}
Text before the object, Markdown fences, or syntax errors make the entire response unsuitable for passing directly to a parser:
Here is the result:
{
"title": "Example"
}
```json
{
"title": "Example"
}
```
{
"title": "Example",
}
JSON also requires double quotes for strings and property names. The expression {'title': 'Example'} is not valid JSON.
Define an unambiguous response contract
Phrases such as “respond in JSON” are not enough: the model still has to decide which fields to return, which types to use, and whether it may add an explanation. Instead, provide an example object separately from the rules for its fields:
Determine the message category.
Return exactly one JSON object in this form:
{
"category": "bug",
"confidence": 0.8,
"summary": "Short description"
}
Rules:
do not add any text before or after the JSON;
do not use Markdown;
return only the category, confidence, and summary fields;
category may only be bug, question, or feature;
confidence must be a number from 0 to 1;
summary must be a string.
Here, "bug" is just an example value, while the allowed enumeration is defined by a separate rule. Do not put "bug | question | feature" directly into the JSON example: the model may interpret the entire string containing the | characters as a valid value.
For optional or missing data, define one specific behavior as well. For example, if an entity list must always be present, define an empty array as the only representation of no results:
If no entities are found, return:
"entities": []
Do not omit the entities field and do not replace the empty array with null.
If a field may contain null, define that explicitly as well:
{
"email": null
}
Do not use null, an empty string, and the string "unknown" interchangeably for the same state. The fewer ways there are to represent one condition, the easier the result is to process.
The instruction “return only valid JSON” is not format validation. A response that will be processed automatically by an application must first be parsed with a JSON parser and then validated for structure and values.
Use structured API output when available
Some language model APIs support a dedicated JSON mode, structured output, or response constraints based on JSON Schema. Such mechanisms are preferable to relying on a text instruction alone because some formatting requirements are enforced through the API itself.
Parameter names, the supported subset of JSON Schema, and other restrictions depend on the provider and API version. There is therefore no universal configuration snippet that works across all services. Check the official documentation for the API you use and define the expected types, required fields, and allowed values there.
Keep server-side validation even when using a structured output mode. A schema constrains the data representation, but the application should still validate the result before using it.
Validate syntax with a real JSON parser
Do not determine whether JSON is valid with a regular expression, by searching for curly braces, or by visual inspection. Pass the original string directly to the standard parser without first stripping Markdown or attempting to “repair” the response.
Python
import json
raw = model_response
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
print(f"Invalid JSON: {exc}")
data = None
Python's standard json.loads() has an important limitation for strict JSON validation: by default, the decoder accepts the special values NaN, Infinity, and -Infinity. These are not valid JSON numeric literals. If you need to reject such constants, use parse_constant:
import json
def reject_constant(value):
raise ValueError(f"Invalid JSON constant: {value}")
try:
data = json.loads(
model_response,
parse_constant=reject_constant,
)
except (json.JSONDecodeError, ValueError) as exc:
print(f"Invalid JSON: {exc}")
data = None
This approach is suitable when you need to validate strict JSON rather than the extended behavior of Python's standard decoder.
JavaScript
let data;
try {
data = JSON.parse(modelResponse);
} catch (error) {
console.error("Invalid JSON:", error);
data = null;
}
JSON.parse() expects the entire supplied string to represent JSON. An explanation before the object or a Markdown fence will cause parsing to fail.
Validate fields and types after parsing
Syntactically valid JSON does not mean that the model followed the contract. For example:
{
"category": "something_else",
"confidence": "high",
"summary": 15
}
This object can be parsed as JSON, but all three values violate the expected schema. A minimal validation in Python might look like this:
allowed_categories = {"bug", "question", "feature"}
if not isinstance(data, dict):
raise ValueError("Expected JSON object")
if data.get("category") not in allowed_categories:
raise ValueError("Unexpected category")
confidence = data.get("confidence")
if not isinstance(confidence, (int, float)) or isinstance(confidence, bool):
raise ValueError("confidence must be a number")
if not 0 <= confidence <= 1:
raise ValueError("confidence is out of range")
if not isinstance(data.get("summary"), str):
raise ValueError("summary must be a string")
For larger contracts, JSON Schema or the application's existing data-modeling system is usually more convenient. At a minimum, validate required fields, their types, enumerations, ranges, and business-logic constraints.
Do not repair the response with string replacements
Attempts to automatically transform an almost-correct response often introduce new errors. For example:
raw = raw.replace("'", '"')
This operation changes not only syntax quotes but also the contents of strings. Similarly, extracting everything between the first { and the last } is unsafe: the text may contain multiple objects or curly braces inside a string value.
It is safer to treat an unparseable response as a contract violation. If the architecture allows regeneration, send the model a short description of the specific error and then validate the entire new result again with the same parser and validator.
For example:
The previous response violated the contract:
the confidence field must be a number from 0 to 1.
Return exactly one JSON object again:
{
"category": "bug",
"confidence": 0.8,
"summary": "Short description"
}
category may only be bug, question, or feature.
Do not add Markdown or explanations.
For a retry, it is usually enough to identify the field that violated the contract, the expected type, or the allowed range. There is no need to copy large parser diagnostic messages into the prompt.
Do not mix machine-readable output with a user-facing explanation
If one process needs JSON while another needs a human-readable explanation, do not place both sequentially in the same response that will later be passed to a JSON parser.
The explanation can be made part of the contract itself:
{
"result": "approved",
"reason": "The request meets the specified conditions."
}
Another option is to generate the user-facing text in a separate request. In both cases, the machine-readable channel remains unambiguous.
Test the contract with edge-case inputs
Before connecting generation to a production workflow, test several types of input: a normal request, empty input, multiline text, quotes inside source data, Unicode, missing required information, and input likely to provoke an explanatory response.
For every case, the sequence should be the same:
- Receive the model's original response string without modifications.
- Pass it to a JSON parser.
- Validate the expected root type: object, array, or another allowed value.
- Validate required fields and types.
- Validate enumerations, ranges, and business constraints.
- Use the data only after validation succeeds.
This tests the actual integration contract. A response that visually resembles JSON in an interface or log does not necessarily mean that a programmatic consumer can safely accept it.
Copyable prompt template
Complete the task and return exactly one JSON object.
Example structure:
{
"status": "success",
"items": [
{
"name": "Example",
"score": 0.8
}
]
}
Rules:
return only JSON without Markdown or explanations;
status may only be success or unknown;
name must be a string;
score must be a number;
if there are no items, items must be an empty array [];
if there is insufficient data, use status unknown and an empty items array;
do not add fields that are not defined by the contract.
Replace the field names, allowed values, and missing-data rules with the requirements of your specific application. If the API supports structured output, move the formal constraints into its schema and keep the prompt focused on describing the meaning of the data.
Final checklist
- The expected JSON root type is defined.
- The type and allowed values are defined for every field.
- Missing data has one unambiguous representation.
- The JSON example does not contain pseudo-values such as
"a | b | c". - The prompt forbids Markdown and text around the object.
- If structured output or JSON Schema is available, constraints are also defined through the API.
- The original response is validated with a standard JSON parser.
- For strict validation in Python,
NaNand infinity values are explicitly rejected. - After parsing, fields, types, enumerations, and ranges are validated.
- An invalid response is not repaired with unreliable string replacements.
- Any regenerated response goes through the same validation process again.
The practical principle is simple: the prompt defines the contract, the API's structured output mode constrains the response format, and the parser and validator determine whether the result can safely be used as machine-readable data.