cite
Generate a citation and BibTeX/RIS exports from source metadata, without an LLM.
Endpoint and method
POST https://metaphrase.msalman.com/api/v1/cite
Authentication
Send Authorization: Bearer <key> and Content-Type: application/json. Create a key at the dashboard. Store it in backend environment variable METAPHRASE_API_KEY; never expose it in a browser, mobile binary, public repository, or widget. See authentication and a working backend proxy.
Request parameters
JSON uses snake_case. Unknown fields are rejected. Omit optional values instead of sending null. Maximum text length: 20,000 Unicode code points.
| Name | Type | Required | Default | Allowed values / range | Max length / items |
|---|---|---|---|---|---|
source_type | string | No | "auto" | auto, url, doi, isbn, manual | — |
source | string | Unless source_type=manual | — | — | 2048 |
style | string | No | "apa" | apa, mla, chicago, harvard, ieee | — |
fields | object | If source_type=manual | — | — | — |
fields.type | string | No | "webpage" | webpage, book, article-journal | — |
fields.title | string | Yes | — | — | 500 |
fields.authors | array | No | [] | — | 50 |
fields.authors[].given | string | No | — | — | 120 |
fields.authors[].family | string | No | — | — | 120 |
fields.authors[].literal | string | No | — | — | 250 |
fields.year | integer | No | — | 1 to 9999 | — |
fields.month | integer | No | — | 1 to 12 | — |
fields.day | integer | No | — | 1 to 31 | — |
fields.url | string | No | — | uri | 2048 |
fields.publisher | string | No | — | — | 250 |
fields.container_title | string | No | — | — | 300 |
fields.volume | string | No | — | — | 40 |
fields.issue | string | No | — | — | 40 |
fields.pages | string | No | — | — | 80 |
fields.doi | string | No | — | — | 255 |
fields.isbn | string | No | — | — | 25 |
fields.accessed_date | string | No | — | date | — |
stream | boolean | No | false | false | — |
source_type: auto recognizes DOI, public HTTP(S) URL or ISBN; manual requires fields instead of source.source: A DOI (plain or doi.org URL), a checksum-valid ISBN-10/13, or a public HTTP(S) page URL. Private networks, credentials and nonstandard ports are rejected.style: apa=APA 7; mla=MLA 9; chicago=Chicago 18 author-date; harvard=Cite Them Right 12; ieee=IEEE.fields: Manual source metadata. Required only for source_type=manual; title is required. Other missing metadata is reported in warnings.stream: Use true for SSE on paraphrase/rewrite, translate or summarize. Other operations require false; alternatives always returns JSON.
Use fields only with source_type=manual; fields.title is required. Every author needs family or literal; month requires year, and day requires a valid month/year date. Omit unknown values instead of sending null. auto recognizes DOI URLs/plain DOI, HTTP(S) URL, or checksum-valid ISBN. URL metadata reads only public HTTP(S) pages on ports 80/443, checks every redirect and pins validated DNS. Use manual fields for blocked, scanned, missing or incomplete metadata. Styles: APA 7, MLA 9, Chicago 18 author-date, Harvard Cite Them Right 12 and IEEE. Copy bibtex into a .bib file or ris into a .ris file.
Full example request
{
"source_type": "manual",
"style": "apa",
"fields": {
"type": "book",
"title": "A clear idea",
"authors": [
{
"given": "Ada",
"family": "Lovelace"
}
],
"year": 2024,
"publisher": "Example Press"
},
"stream": false
}
Full example response
HTTP 200, application/json. This is an illustrative response; writing, source metadata, token counts and cost can vary. request_id is a server-generated UUID. Cache hits use usage.cached=true and report zero new tokens/cost. usage_estimated=true means the provider did not supply exact token counts.
{
"request_id": "019b8df0-1234-4000-8000-000000000006",
"tool": "cite",
"result": {
"text": "Lovelace, A. (2024). A clear idea. Example Press.",
"style": "apa",
"style_name": "APA 7",
"source_type": "manual",
"metadata": {
"type": "book",
"title": "A clear idea",
"authors": [
{
"given": "Ada",
"family": "Lovelace"
}
],
"year": 2024,
"publisher": "Example Press"
},
"bibtex": "@book{mpfda3a81f9b10,\n author = {Lovelace, Ada},\n title = {A clear idea},\n year = {2024},\n publisher = {Example Press}\n}\n",
"ris": "TY - BOOK\nAU - Lovelace, Ada\nTI - A clear idea\nPY - 2024\nPB - Example Press\nER -\n",
"warnings": []
},
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
"estimated_cost_usd": 0,
"usage_estimated": false,
"cached": false,
"provider": "self_hosted",
"model": "citation.js_0.9.0"
},
"input_words": 3,
"output_words": 8
}
Errors: cause and fix
Every error uses this JSON shape and a link to its exact fix:
{
"error": {
"code": "unauthorized",
"message": "A valid API key is required.",
"docs_url": "https://metaphrase.msalman.com/docs/errors#unauthorized"
}
}
| Code | HTTP | Cause | Fix |
|---|---|---|---|
invalid_request | 400 | Check the request fields and their allowed values. | Send the documented JSON fields with the correct types. Unknown fields are rejected. |
invalid_json | 400 | The request body is not valid JSON. | Serialize the request with JSON.stringify or your language's JSON encoder. |
unsupported_media_type | 415 | Send application/json content. | Set Content-Type: application/json. File uploads use their separate documented route. |
body_too_large | 413 | The request body exceeds the allowed size. | Reduce the payload and split long documents at sentence boundaries. |
text_too_long | 413 | The text exceeds the configured character limit. | Use the max_input_chars value in the API specification and split longer documents. |
unauthorized | 401 | A valid API key is required. | Send Authorization: Bearer YOUR_KEY. Create a new key if the old one was revoked. |
forbidden | 403 | This action is not available to this account. | Check account status and the key's access permissions. |
rate_limited | 429 | Too many requests. Please wait and try again. | Wait for Retry-After seconds and use exponential backoff with jitter. |
quota_exceeded | 429 | The monthly usage quota has been reached. | Wait for the next UTC calendar month or ask an administrator to change the plan. |
service_unavailable | 503 | A required service is temporarily unavailable. | Wait and retry. The operator should check Redis, PostgreSQL, or the tool's health. |
not_found | 404 | This resource does not exist. | Check the API version, endpoint name, and resource identifier. |
method_not_allowed | 405 | This HTTP method is not supported. | Use POST for processing endpoints and the method documented for dashboard routes. |
internal_error | 500 | The request could not be completed. | Retry once. If it persists, provide the X-Request-Id header to the operator. |
invalid_source | 422 | The source could not be used. | Check the DOI, ISBN, or public HTTP URL, or use manual citation fields. |
source_unavailable | 422 | Source metadata could not be retrieved. | Try a DOI or ISBN, or enter the source details manually. |
For streaming requests, failures after HTTP headers are sent arrive as an SSE error event with this same error object. Never treat partial text as a completed result or append output from a retry.
Rate limits and quotas
Defaults: Free 15 requests per 60 seconds and 500 accepted calls per key per UTC calendar month; Pro placeholders 120/60 seconds and 25,000/month. The operator can change plans; response headers are authoritative. Calls to all tools share a key's limits. Accepted cache hits and upstream failures consume quota. Authentication/schema failures and provider-not-configured preflight failures do not. X-RateLimit-Remaining, X-RateLimit-Reset, X-Quota-Remaining and X-Quota-Reset show remaining capacity and Unix-second reset times. For HTTP 429 wait Retry-After seconds; quota/budget limits need a reset or operator action. Requests may also receive 429 when the shared processing slots are busy. See retries and long texts.