Validate, score, and remediate merchant product feeds, returning clean exports plus merchant-facing readiness reports for AI/agent consumption.
As AI assistants become a product-discovery and shopping interface, catalog quality becomes part of machine-readable discoverability. This project explores that idea with a transparent pipeline: take a messy feed, normalize it into a stable product shape, explain what blocks agent use, and propose deterministic fixes.
The repository is a learning and experimentation project. It does not promise SEO ranking, product inclusion, or compatibility with any platform, and it is not an official validator from OpenAI, Shopify, or another vendor.
All committed examples are synthetic. Never commit merchant, affiliate-network, client, or account exports.
- Language: Python 3.12
- Framework: FastAPI + Uvicorn
- Package management: uv
- Linting/formatting: Ruff
Prereqs: Python 3.12 and uv.
uv venv --python 3.12
source .venv/bin/activate
uv sync
uv run uvicorn main:app --reload
uv run ruff check .
uv run ruff format
uv run pytest
uv run pytest tests/test_validator.py::test_name # single test
Full pipeline: validate + sanitize. Returns cleaned feed + QA report.
Validate only; returns qa_report (no cleaning). Use for CI/CD or pre-merge checks.
Sanitize only; returns clean_openai_feed (no scoring). Use when you want cleaned output only.
All endpoints accept the same input formats:
Input formats (pick one):
| Format | How to send |
|---|---|
JSON array or {"rows": [...]} |
Content-Type: application/json body |
| JSON Lines | Content-Type: application/json body |
| CSV file | Multipart form field file |
| Remote URL | source_url query param or body field |
Optional param: emit_jsonld=true — include schema.org JSON-LD per product.
Optional param: profile=openai_chatgpt — choose the readiness profile. More profiles can be added without changing the API shape.
Example (JSON body):
curl -X POST http://localhost:8000/v1/feed/qa \
-H "Content-Type: application/json" \
-d '{"rows": [{"id": "SKU-1", "title": "Widget", "price": "9.99 USD", ...}]}'Example (file upload):
curl -X POST http://localhost:8000/v1/feed/qa \
-F "file=@feeds/good_example.json" \
-F "emit_jsonld=false"Response schema:
{
"clean_openai_feed": {
"json": [ /* normalized product rows */ ],
"csv": "/* CSV string */"
},
"clean_feed": {
"json": [ /* same rows as clean_openai_feed */ ],
"csv": "/* same CSV string */"
},
"qa_report": {
"agent_readiness_score": 0.0,
"blocking_pass_rate": 0.0,
"eligible_product_rate": 0.0,
"profile": "openai_chatgpt",
"rules_version": "2026.04.1",
"subscores": {
"identifiers": 0.0,
"offer_integrity": 0.0,
"content_quality": 0.0,
"compliance": 0.0
},
"coverage": { /* per-field coverage percentages */ },
"issue_summary": {
"total_issues": 0,
"blocking_count": 0
},
"issues": [
{
"row": 0,
"field": "price",
"type": "missing",
"message": "...",
"bucket": "blocking",
"severity": "critical",
"is_blocking": true
}
],
"generated_at": "2024-01-01T00:00:00Z"
},
"result_metadata": {
"profile": "openai_chatgpt",
"spec_path": "docs/openai_chatgpt_profile.md",
"proposed_change_rows": 12
},
"proposed_changes": [
{
"row": 0,
"id": "SKU-1",
"change_count": 1,
"changes": [
{
"field": "link",
"op": "replace",
"before": "example.com/p/1",
"after": "https://example.com/p/1",
"issue_types": ["normalized"],
"reason": "use a full URL including the https:// scheme"
}
]
}
],
"schema_org_jsonld": [ /* schema.org Product objects, if emit_jsonld=true */ ]
}Scoring: 20% identifiers · 40% offer integrity · 30% content quality · 10% compliance (material, weight, return_policy, return_window). Includes blocking_pass_rate (rows passing all required fields).
Each persisted run also writes:
merchant_report.json— structured executive summary, grouped issues, top actions, coverage gapsmerchant_report.md— human-readable summary suitable for sharing with merchants or agencies
Retrieve them through:
GET /v1/runs/{run_id}/reportGET /v1/runs/{run_id}/report?format=mdpf export <run_id> --format report-jsonpf export <run_id> --format report-md
See the project-defined
openai_chatgpt readiness profile and the
canonical platform references. This is an independent
experimental validator, not an official platform certification tool.
For a concise snapshot of the current implementation, see docs/STATUS.md.
The current service is designed for local development and trusted inputs. It is not a hardened multi-tenant SaaS. In particular, production deployments should add authentication, tenant isolation, secret management, URL egress controls, request/response size limits, and an explicit data-retention policy.
Shopify tokens supplied to the API, CLI, or MCP tool are used for one sync and
are not persisted. Feed runs do persist their raw and normalized rows locally
under ignored runs/ and runs.db paths.
Please report security concerns privately rather than opening an issue that contains credentials or customer data.
Contributions are welcome; see CONTRIBUTING.md. This project is released under the MIT License.