{"openapi":"3.0.3","info":{"title":"FetchMD API","version":"1.0.0","description":"Convert any public URL to clean Markdown optimized for LLMs, RAG pipelines, and AI agents. Strips HTML boilerplate, reduces token count by ~80%. Free tier: 100 conversions/month. Paid plans from $9/mo.","contact":{"email":"hello@fetchmd.dev","url":"https://fetchmd.dev"},"license":{"name":"Commercial","url":"https://fetchmd.dev/pricing"}},"servers":[{"url":"https://fetchmd.dev","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Your FetchMD API key (fmd_...). Get a free key at https://fetchmd.dev/api-key"}},"schemas":{"ConvertResponse":{"type":"object","required":["markdown","url","title","tokenEstimate","fetchTimeMs","htmlSize","markdownSize","compressionRatio","cached"],"properties":{"markdown":{"type":"string","description":"Clean Markdown version of the page content"},"url":{"type":"string","format":"uri","description":"The final URL after redirects"},"title":{"type":"string","description":"Page title extracted from <title> or <h1>"},"tokenEstimate":{"type":"integer","description":"Estimated LLM token count (chars/4)"},"fetchTimeMs":{"type":"integer","description":"Time to fetch the URL in milliseconds"},"htmlSize":{"type":"integer","description":"Original HTML size in bytes"},"markdownSize":{"type":"integer","description":"Output Markdown size in bytes"},"compressionRatio":{"type":"number","description":"Percentage size reduction vs raw HTML"},"cached":{"type":"boolean","description":"Whether this response was served from cache (cache hits don't consume quota)"},"quota":{"$ref":"#/components/schemas/QuotaInfo"}}},"BulkResult":{"type":"object","required":["url","markdown","title","tokenEstimate","compressionRatio"],"properties":{"url":{"type":"string","format":"uri"},"markdown":{"type":"string"},"title":{"type":"string"},"tokenEstimate":{"type":"integer"},"compressionRatio":{"type":"number"},"error":{"type":"string","description":"Present only if this URL failed"}}},"BulkResponse":{"type":"object","required":["results","succeeded","failed","totalTokens","quota"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/BulkResult"}},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"totalTokens":{"type":"integer"},"quota":{"$ref":"#/components/schemas/QuotaInfo"},"warning":{"type":"string","description":"Set if some URLs were skipped due to quota"}}},"QuotaInfo":{"type":"object","required":["plan","used","limit","remaining","resetsAt"],"properties":{"plan":{"type":"string","enum":["free","starter","pro","scale"]},"used":{"type":"integer","description":"Conversions used this month"},"limit":{"type":"integer","description":"Monthly conversion limit"},"remaining":{"type":"integer","description":"Conversions remaining this month"},"resetsAt":{"type":"string","description":"When the monthly quota resets (UTC)"},"batchLimit":{"type":"integer","description":"Max URLs per /api/bulk call on this plan"}}},"JobResponse":{"type":"object","required":["jobId","status","totalUrls","submittedAt"],"properties":{"jobId":{"type":"string","description":"Unique job identifier"},"status":{"type":"string","enum":["processing","complete","failed"]},"totalUrls":{"type":"integer"},"submittedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"pollUrl":{"type":"string","description":"URL to poll for results"},"results":{"type":"array","items":{"$ref":"#/components/schemas/BulkResult"}},"summary":{"type":"object","properties":{"succeeded":{"type":"integer"},"failed":{"type":"integer"},"totalTokens":{"type":"integer"}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"plan":{"type":"string"},"limit":{"type":"integer"},"used":{"type":"integer"},"quota":{"$ref":"#/components/schemas/QuotaInfo"}}}}},"paths":{"/api/convert":{"post":{"operationId":"convertUrl","summary":"Convert URL to Markdown","description":"Fetch any public URL and return clean Markdown. Strips ads, scripts, navigation, and boilerplate. Returns ~80% fewer tokens than raw HTML. Cache hits don't consume quota. Anonymous use allowed (web UI only).","tags":["Core"],"security":[{"apiKey":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"The URL to convert to Markdown","example":"https://docs.anthropic.com/en/docs/about-claude/models/overview"}}}}}},"responses":{"200":{"description":"Successful conversion","headers":{"X-FetchMD-Remaining":{"schema":{"type":"integer"},"description":"Conversions remaining this month"},"X-FetchMD-Limit":{"schema":{"type":"integer"},"description":"Monthly conversion limit"},"X-FetchMD-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp when quota resets"},"X-FetchMD-Plan":{"schema":{"type":"string"},"description":"Your current plan"},"X-FetchMD-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Cache status"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"URL returned non-HTML or error status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until quota resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"URL fetch timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/bulk":{"post":{"operationId":"bulkConvert","summary":"Batch convert URLs to Markdown","description":"Convert multiple URLs in parallel. Requires an API key. Batch limits: free=20, starter=200, pro/scale=500 per call. Monthly quota applies across all endpoints.","tags":["Core"],"security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["urls"],"properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1,"maxItems":500,"description":"URLs to convert. Max per call depends on your plan.","example":["https://example.com/article-1","https://example.com/article-2"]}}}}}},"responses":{"200":{"description":"Batch complete (some individual URLs may have errors — check each result)","headers":{"X-FetchMD-Remaining":{"schema":{"type":"integer"}},"X-FetchMD-Limit":{"schema":{"type":"integer"}},"X-FetchMD-Reset":{"schema":{"type":"integer"}},"X-FetchMD-Plan":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResponse"}}}},"400":{"description":"Invalid request or batch limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs":{"post":{"operationId":"submitJob","summary":"Submit async batch job","description":"Submit up to 200 URLs for async processing. Returns a jobId immediately. Poll /api/jobs/{jobId} for results. Ideal for large batches that would exceed the 30s timeout of synchronous /api/bulk.","tags":["Jobs"],"security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["urls"],"properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1,"maxItems":200}}}}}},"responses":{"202":{"description":"Job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["processing"]},"totalUrls":{"type":"integer"},"pollUrl":{"type":"string"},"submittedAt":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs/{jobId}":{"get":{"operationId":"getJob","summary":"Poll async job status","description":"Poll for the status and results of an async batch job. Results are available for 1 hour after completion.","tags":["Jobs"],"security":[{"apiKey":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"Job ID returned by POST /api/jobs"}],"responses":{"200":{"description":"Job status (check status field — may still be processing)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found (may have expired after 1 hour)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/quota":{"get":{"operationId":"getQuota","summary":"Get quota status","description":"Check your current monthly usage, remaining conversions, and when your quota resets.","tags":["Account"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Quota info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaInfo"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"tags":[{"name":"Core","description":"URL conversion endpoints"},{"name":"Jobs","description":"Async batch processing"},{"name":"Account","description":"Quota and account management"}]}