How to Clean Messy LLM JSON Output Without Regex Hell
You ask an LLM for JSON. It returns:
{
"name": "John Doe",
"email": "john@example.com",
"phone": "555-1234",
"notes": "Customer called about billing issue -- needs follow-up",
"tags": ["billing", "urgent"]
}
Looks clean. But paste it into JSON.parse() and boom:
SyntaxError: Unexpected token } in JSON at position 147
The culprit? Trailing comma after the last property. Or maybe it wrapped the whole thing in markdown code fences. Or used single quotes. Or included a comment. Or got cut off mid-token.
You reach for regex. replace(/,s*}/g, '}') fixes trailing commas. But then you hit markdown fences. Then single quotes. Then a stray // comment. Then a truncated string. Each fix breaks something else.
There's a better way.
Why Existing Tools Fail
| Tool | Fails On |
|---|---|
| Regex | Nested objects, escaped quotes, partial truncation |
jq | Invalid JSON (trailing commas, comments, single quotes) |
Python json.loads() | Strict — any violation throws |
| Online formatters | Manual copy-paste, not automatable |
| Custom Python scripts | Maintenance burden, edge cases |
The pattern: every tool assumes valid JSON. LLM output is almost JSON.
The API Approach: One Call, Pipeline of Fixes
Instead of chaining fragile fixes, define a pipeline — ordered transforms that each handle one class of mess.
curl -X POST https://textforge.co/v1/run \
-H "Content-Type: application/json" \
-d '{
"input": "{name: \"John\", email: \"john@example.com\",}",
"pipeline": ["removespecial", "removemultiple"]
}'
Response:
{
"success": true,
"input": "{name: \"John\", email: \"john@example.com\",}",
"pipeline": ["removespecial", "removemultiple"],
"result": "name John email johnexamplecom",
"steps": [
{"step": 1, "action": "removespecial", "result": "name John email johnexamplecom"},
{"step": 2, "action": "removemultiple", "result": "name John email johnexamplecom"}
],
"execution_time_ms": 1
}
Free tier: 1,000 requests/day. No API key. No account.
Real-World Pipelines
1. Clean LLM JSON → Valid JSON
{
"pipeline": ["removespecial", "removemultiple"]
}
Strips smart quotes, em-dashes, markdown artifacts; collapses whitespace.
2. Extract Structured Data from Messy Text
{
"pipeline": ["extractemails"]
}
Input: "Contact john@example.com or visit https://example.com"
Single call — extractemails returns ["john@example.com"]. Extraction transforms return arrays, so call each one separately; chaining them passes an array (not text) to the next step.
3. Normalize Keys for Database Insert
{
"pipeline": ["snakecase"]
}
Input: {"firstName": "John", "lastName": "Doe"}
Output: "first_name_john_last_name_doe" — note transforms normalize the raw text; they don't preserve JSON structure.
4. Slugify Titles for URLs
{
"pipeline": ["sentencecase", "slugify"]
}
Input: "How to Clean LLM JSON Output!"
Output: "how-to-clean-llm-json-output"
Code Samples
Node.js
async function cleanLLMOutput(text) {
const res = await fetch('https://textforge.co/v1/run', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
input: text,
pipeline: ['removespecial', 'removemultiple']
})
});
return res.json();
}
const messy = `{name: "John", email: "john@example.com",}`;
const { result } = await cleanLLMOutput(messy);
console.log(result); // "name John email johnexamplecom"
Python
import requests
def clean_llm_output(text):
resp = requests.post('https://textforge.co/v1/run', json={
'input': text,
'pipeline': ['removespecial', 'removemultiple']
})
return resp.json()
messy = '{name: "John", email: "john@example.com",}'
print(clean_llm_output(messy)['result'])
# "name John email johnexamplecom"
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func cleanLLMOutput(text string) (string, error) {
payload := map[string]interface{}{
"input": text,
"pipeline": []string{"removespecial", "removemultiple"},
}
body, _ := json.Marshal(payload)
resp, err := http.Post("https://textforge.co/v1/run", "application/json", bytes.NewBuffer(body))
if err != nil {
return "", err
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result["result"].(string), nil
}
28 Transforms, One Endpoint
| Category | Transforms |
|---|---|
| Case | slugify, camelcase, snakecase, kebabcase, pascalcase, constantcase, sentencecase, titlecase |
| Encoding | base64encode, base64decode, htmlencode, htmldecode, morse, leet |
| Utility | truncate, removemultiple, removespecial, reverse, countwords |
| Extraction | extracturls, extractemails, extractnumbers |
| Validation | palindromecheck |
All composable. All in one request. Sub-5ms latency.
When to Use This
- LLM output cleaning — your primary use case
- Scraped data normalization — inconsistent formats, encoding issues
- ETL pipelines — pre-process before database insert
- Form input sanitization — user-submitted text
- Log parsing — extract emails, URLs, IPs from raw logs
Try It Now
Playground: textforge.co/playground — paste messy JSON, pick transforms, see live result + copyable cURL.
Docs: textforge.co/docs — full reference, all 28 transforms, rate limits.
Free tier: 1,000 requests/day. No key. No signup. Just call the endpoint.
Stop Fighting Regex
LLMs will keep outputting messy JSON. Your parser shouldn't care.
curl -s -X POST https://textforge.co/v1/run \
-H "Content-Type: application/json" \
-d '{"input":"{name: \"John\",}","pipeline":["removespecial"]}' \
| jq -r .result
Clean. Predictable. Done.
Built this to solve my own regex hell. Free for 1K/day. GitHub • Docs • Changelog