# sqlai.dev — SQL verification for AI agents > Executes SQL against a real ephemeral in-memory database (SQLite via WASM) and > returns ground truth: rows, typed errors with positions and suggestions, query > plans, or result-set diffs. Nothing is LLM-generated; the response is what the > engine actually did. Per-call payment via x402; 20 free calls/day > per client; no accounts or API keys. ## Endpoints - MCP (Streamable HTTP, stateless): https://mcp.sqlai.dev/mcp - Plain HTTP: POST https://mcp.sqlai.dev/api/ with a JSON body of tool arguments - OpenAPI description: https://sqlai.dev/openapi.yaml - Human docs: https://sqlai.dev/docs/ - Free browser demo (client-side WASM, SQLite + DuckDB): https://sqlai.dev/ ## Tools - run_sql ($0.005): schema (DDL), query, seed? ({"table":[{col:val}]}), engine? → rows (max 500, truncation flagged), columns+types, row_count, execution_ms, empty_dataset flag, dialect_notes. - validate_sql ($0.003): schema, query → ok + referenced_tables, or typed error. Parses and binds via the engine without executing. - explain_plan ($0.003): schema, query → engine-native plan lines + full-scan warnings. - run_sql_batch ($0.02): schema, queries (max 10), seed? → array of run_sql results; each query runs in its OWN fresh database (no cross-query state). - diff_results ($0.008): schema, seed?, query_a, query_b → equal? (multiset, order-insensitive), order_differs, only_in_a/only_in_b (capped 50), row counts. ## Error taxonomy (branch on error.type, not message text) unknown_column | unknown_table | syntax | type_mismatch | constraint | timeout | resource_limit | unsupported — plus message, position {line,col}|null, and a rule-based suggestion (edit distance against the submitted schema; never an LLM). ## Semantics and limits - Fresh in-memory database per call, destroyed after. Submitted SQL/data never stored. - One statement per query slot; stacked statements rejected (use run_sql_batch). - Limits: 5s CPU, 10MB seed JSON, 256KB schema, 64KB query, 500 rows returned. - Empty seed → runs against empty tables; response sets empty_dataset: true. - Engine: SQLite 3.49 server-side. DuckDB is browser-demo only (Worker size limits). dialect_notes flag constructs behaving differently on Postgres/MySQL/DuckDB/SQL Server. - Responses are strict JSON with version field ("1"). ## Payment (x402) Unpaid request past the free tier → HTTP 402 with {x402Version:1, accepts:[...]} (scheme "exact", USDC). Retry with X-PAYMENT header; settlement receipt returned in X-PAYMENT-RESPONSE. A query error after payment is a delivered result — no refunds for query errors; our infra faults are not charged.