detect
Estimate AI-like writing cues overall and for each sentence; results may be wrong.
Endpoint and method
POST https://metaphrase.msalman.com/api/v1/detect
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 |
|---|---|---|---|---|---|
text | string | Yes | — | — | 20000 |
stream | boolean | No | false | false | — |
text: Plain text. Limit counts Unicode code points. At least 50 words are required. Scores are uncalibrated estimates, not authorship evidence.stream: Use true for SSE on paraphrase/rewrite, translate or summarize. Other operations require false; alternatives always returns JSON.
This is an uncalibrated LLM heuristic, not a validated probability of authorship. At least 50 words are required. Always display disclaimer with every result. Do not make decisions about a person from this score alone. Sentence offsets and lengths use UTF-16 units; very long sentences may be divided into bounded spans.
Full example request
{
"text": "The research team spent several long months carefully studying how local people use public spaces in their neighborhood and listening to residents describe their daily routines. They found that shade, comfortable seating, and safe crossings mattered more to participants than expensive decorative features, so the final proposal focused on those practical needs.",
"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-000000000003",
"tool": "detect",
"result": {
"text": "AI-like writing estimate: 50%\n\nAI detection is an uncalibrated estimate and may be wrong. Human writing can be flagged as AI, and AI writing can be missed. Never use this result alone to make decisions about a person.\n\n[50%] The research team spent several long months carefully studying how local people use public spaces in their neighborhood and listening to residents describe their daily routines.\n\n[50%] They found that shade, comfortable seating, and safe crossings mattered more to participants than expensive decorative features, so the final proposal focused on those practical needs.",
"ai_estimate": 50,
"method": "llm_heuristic",
"disclaimer": "AI detection is an uncalibrated estimate and may be wrong. Human writing can be flagged as AI, and AI writing can be missed. Never use this result alone to make decisions about a person.",
"sentences": [
{
"text": "The research team spent several long months carefully studying how local people use public spaces in their neighborhood and listening to residents describe their daily routines.",
"offset": 0,
"length": 177,
"ai_estimate": 50
},
{
"text": "They found that shade, comfortable seating, and safe crossings mattered more to participants than expensive decorative features, so the final proposal focused on those practical needs.",
"offset": 178,
"length": 184,
"ai_estimate": 50
}
]
},
"usage": {
"input_tokens": 140,
"output_tokens": 80,
"total_tokens": 220,
"estimated_cost_usd": 0.000054,
"usage_estimated": false,
"cached": false,
"provider": "primary",
"model": "gpt-6-luna"
},
"input_words": 52,
"output_words": 93
}
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. |
budget_exceeded | 429 | The writing service has reached its configured spending limit. | The operator must review usage and adjust the configured budget before more paid requests can run. |
provider_not_configured | 503 | The writing provider is not configured yet. | The operator must configure LLM_PRIMARY_API_KEY or a fallback provider in .env and recreate the web container. |
upstream_unavailable | 503 | The writing provider is temporarily unavailable. | Retry with backoff. The operator can inspect provider availability without logging submitted text. |
upstream_timeout | 504 | The writing provider took too long to respond. | Retry a shorter input or wait before retrying. Do not append a retry to partial streamed output. |
upstream_error | 502 | The writing provider returned an invalid response. | Retry once with a shorter input. If it continues, the operator should check the configured model's compatibility. |
output_truncated | 502 | The response reached its output limit before finishing. | Use a shorter input or ask the operator to adjust the tool's output cap and chunk size. |
text_too_short | 422 | More text is needed for a meaningful estimate. | Submit at least the documented minimum word count. Very short texts cannot be assessed meaningfully. |
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.