openapi: "3.0.3" info: title: AnyMD API description: | Convert documents (PDF, DOCX, PPTX, XLSX, images and 20+ other formats) to clean Markdown. Designed for RAG ingestion pipelines, LLM preprocessing, and content migration. Authentication is via `Authorization: Bearer ` header. Create a key from the [AnyMD dashboard](/account). version: "1.0.0" contact: name: AnyMD url: https://anymd.net servers: - url: https://anymd.net description: Production paths: /api/convert: post: summary: Convert a document to Markdown description: | Upload a single file and receive its contents as plain Markdown (`text/markdown`). Programmatic pipeline entry-point — returns raw text, not JSON. **Pricing:** each conversion consumes pages from your plan's monthly quota. One 1-page PDF = 1 page. A 100-page DOCX = 100 pages. operationId: convertDocument tags: [Conversion] security: - ApiKeyAuth: [] requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary description: The document file to convert. required: [file] responses: "200": description: The converted Markdown text. content: text/markdown: schema: type: string description: Clean Markdown output. example: "# Meeting Notes\n\n## Agenda\n- Item 1\n- Item 2" "400": description: Bad request — no file uploaded or unsupported format. content: application/json: schema: $ref: "#/components/schemas/Error" "401": description: Missing or invalid API key. content: application/json: schema: $ref: "#/components/schemas/Error" "415": description: Unsupported file format. content: application/json: schema: $ref: "#/components/schemas/Error" "429": description: Rate limit exceeded (anonymous endpoints). content: application/json: schema: $ref: "#/components/schemas/Error" "500": description: Internal server error. content: application/json: schema: $ref: "#/components/schemas/Error" /api/convert/batch: post: summary: Convert multiple documents description: | Upload any number of `file` fields in a single multipart request. Each file is converted independently and returned alongside its filename. Failed conversions appear in the `failures` array with their HTTP status code. operationId: convertBatch tags: [Conversion] security: - ApiKeyAuth: [] requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary description: One or more files. Repeat the `file` field for each document. required: [file] responses: "200": description: | Batch conversion results. Each file's entry contains filename, format, byte counts, latency, and the full Markdown output. content: application/json: schema: type: object properties: results: type: array items: $ref: "#/components/schemas/ConversionResult" failures: type: array items: type: object properties: filename: type: string status: type: integer "400": description: No files uploaded. content: application/json: schema: $ref: "#/components/schemas/Error" "401": description: Missing or invalid API key. content: application/json: schema: $ref: "#/components/schemas/Error" "500": description: Internal server error. content: application/json: schema: $ref: "#/components/schemas/Error" /api/convert/full: post: summary: Convert a document with embedded assets description: | Like `/api/convert`, but returns JSON with the rendered Markdown *and* every embedded image / asset base64-encoded. PDFs have no document model in anydoc and return an empty `assets` array. operationId: convertDocumentFull tags: [Conversion] security: - ApiKeyAuth: [] requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary description: The document file to convert. required: [file] responses: "200": description: Full conversion result with base64-encoded assets. content: application/json: schema: allOf: - $ref: "#/components/schemas/ConversionResult" - type: object properties: assets: type: array description: Embedded images and other binary assets. items: $ref: "#/components/schemas/Asset" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "500": $ref: "#/components/responses/InternalError" /api/usage: get: summary: Get API key usage and limits description: | Returns the current billing period's cumulative page usage, the effective pages limit (plan default or admin override), remaining pages, the plan name, and the max upload file size. operationId: getUsage tags: [Account] security: - ApiKeyAuth: [] responses: "200": description: Usage information. content: application/json: schema: $ref: "#/components/schemas/Usage" "401": $ref: "#/components/responses/Unauthorized" "500": $ref: "#/components/responses/InternalError" /api/history: get: summary: Get recent conversions description: | Returns the last 50 conversions for the authenticated API key, newest first. Each entry carries filename, format, size, latency, page count, success flag, and timestamp. operationId: getHistory tags: [Account] security: - ApiKeyAuth: [] responses: "200": description: Recent conversion history. content: application/json: schema: type: object properties: conversions: type: array items: $ref: "#/components/schemas/ConversionRecord" "401": $ref: "#/components/responses/Unauthorized" "500": $ref: "#/components/responses/InternalError" /api/capabilities: get: summary: Get supported formats and plan limits description: | Returns the list of supported file extensions, the billing model, and the per-plan limits (monthly pages, max upload size). No auth required beyond a Bearer token header (any valid key works). operationId: getCapabilities tags: [Info] security: - ApiKeyAuth: [] responses: "200": description: Capabilities information. content: application/json: schema: $ref: "#/components/schemas/Capabilities" "401": $ref: "#/components/responses/Unauthorized" /api/me: get: summary: Check authentication status description: | Lightweight auth probe. Returns `{"authed": true}` if the request carries a valid session cookie; `{"authed": false}` otherwise. Useful for the browser-side converter demo to decide whether to show "Login" or proceed directly. operationId: getMe tags: [Account] responses: "200": description: Authentication status. content: application/json: schema: type: object properties: authed: type: boolean description: Whether the request has a valid session. /api/keys: post: summary: Create a new API key description: | Creates a new Free-plan API key for the currently logged-in account. Requires a valid session cookie (browser login). The raw key is returned once in an HTML fragment — it is never stored server-side. operationId: createApiKey tags: [Account] requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: type: string description: Human-friendly label for the key (optional, defaults to "default"). responses: "200": description: HTML fragment with the raw key (shown once). content: text/html: schema: type: string /api/keys/{id}/delete: post: summary: Delete an API key description: | Permanently removes an API key. The logged-in account must own the key. The old token stops authenticating immediately. operationId: deleteApiKey tags: [Account] parameters: - name: id in: path required: true schema: type: string description: The API key ID to delete. responses: "200": description: Key deleted. "401": $ref: "#/components/responses/Unauthorized" "404": description: Key not found or not owned by this account. content: application/json: schema: $ref: "#/components/schemas/Error" "500": $ref: "#/components/responses/InternalError" components: securitySchemes: ApiKeyAuth: type: http scheme: bearer description: | Create an API key from the [AnyMD dashboard](/account). Pass it as `Authorization: Bearer `. schemas: Error: type: object properties: error: type: string description: Human-readable error message. required: [error] ConversionResult: type: object properties: filename: type: string description: Original uploaded filename. format: type: string description: Detected source format (e.g. "pdf", "docx"). bytes_in: type: integer description: Size of the uploaded file in bytes. bytes_out: type: integer description: Size of the generated Markdown in bytes. latency_ms: type: number format: float description: Server-side conversion time in milliseconds. markdown: type: string description: The converted Markdown content. required: [filename, format, bytes_in, bytes_out, latency_ms, markdown] Asset: type: object properties: id: type: string description: Unique asset identifier within the document. media_type: type: string description: MIME type (e.g. "image/png", "image/jpeg"). origin_part: type: string description: The document part that contained this asset. filename: type: string description: Suggested filename for the asset. data: type: string format: byte description: Base64-encoded binary content. bytes: type: integer description: Size of the binary content in bytes. ConversionRecord: type: object properties: filename: type: string format: type: string ok: type: boolean description: Whether the conversion succeeded. bytes_in: type: integer bytes_out: type: integer latency_ms: type: number format: float pages: type: integer created_at: type: string format: date-time required: [filename, format, ok, bytes_in, bytes_out, latency_ms, pages, created_at] Usage: type: object properties: plan: type: string enum: [free, pro] description: Current plan tier. billing: type: string enum: [pages] description: Billing model (always pages-based currently). used_pages: type: integer description: Pages consumed this billing period. pages_limit: type: integer description: Effective monthly pages limit (plan default or admin override). plan_default_pages_limit: type: integer description: Default monthly pages limit for this plan. remaining_pages: type: integer description: Pages remaining in the current period. period: type: string format: date-time description: Billing period reset timestamp (ISO 8601). max_upload_bytes: type: integer description: Maximum file size in bytes for this plan. required: [plan, billing, used_pages, pages_limit, plan_default_pages_limit, remaining_pages, period, max_upload_bytes] Capabilities: type: object properties: formats: type: array description: List of supported file extensions. items: type: string billing: type: string enum: [pages] plans: type: object properties: free: type: object properties: monthly_pages_limit: type: integer max_upload_bytes: type: integer pro: type: object properties: monthly_pages_limit: type: integer max_upload_bytes: type: integer required: [formats, billing, plans] responses: BadRequest: description: Bad request. content: application/json: schema: $ref: "#/components/schemas/Error" Unauthorized: description: Missing or invalid API key. content: application/json: schema: $ref: "#/components/schemas/Error" RateLimited: description: Rate limit exceeded. content: application/json: schema: $ref: "#/components/schemas/Error" InternalError: description: Internal server error. content: application/json: schema: $ref: "#/components/schemas/Error"