Developers
Lensdrop developer docs
Last updated August 23, 2026
Lensdrop is a photo delivery tool for photographers, not a developer platform. Its one public API is this read-only content API: every marketing page as markdown, an llms.txt index, and the discovery files agents expect. It needs no key, no account, and no registration. Uploads, payments, and gallery delivery have no third-party API; see what is not public.
Machine-readable version of this page: /developers.md. Full API description: /openapi.json.
Discovery files
| Path | Type | What it is |
|---|---|---|
/llms.txt | text/markdown | Agent-readable index of Lensdrop's public content (llmstxt.org). |
/llms-full.txt | text/markdown | Every public page as one markdown document. |
/openapi.json | application/openapi+json | OpenAPI 3.1 description of the content API, with operationIds, typed errors, and headers. |
/.well-known/api-catalog | application/linkset+json | RFC 9727 API catalog pointing at the spec and the docs. |
/auth.md | text/markdown | Auth.md: how (and whether) agents authenticate. Short answer: anonymous. |
/developers.md | text/markdown | This page, as markdown. |
/sitemap.xml | application/xml | Every indexable page. |
/robots.txt | text/plain | Crawl rules. AI crawlers are allowed by name; galleries and the signed-in app are not crawlable. |
Quickstart
Everything is a plain anonymous GET on https://lensdrop.app. Every markdown page is also reachable by requesting its HTML URL with Accept: text/markdown.
Read the index
curl https://lensdrop.app/llms.txtFetch a page as markdown by URL
curl https://lensdrop.app/pricing.mdOr by content negotiation on the HTML URL
curl -H "Accept: text/markdown" https://lensdrop.app/pricingGet the OpenAPI description
curl https://lensdrop.app/openapi.jsonSee an error (typed, RFC 9457)
curl -i https://lensdrop.app/blog/no-such-post.mdEndpoints
Operation ids and parameter schemas (including the valid slugs) are in openapi.json, ready for function-calling tool definitions.
| Operation | operationId | Summary | Returns |
|---|---|---|---|
GET /llms.txt | getLlmsTxt | Index of Lensdrop's agent-readable content | text/markdown |
GET /llms-full.txt | getLlmsFullTxt | The full agent-readable corpus in one document | text/markdown |
GET /index.md | getHomeMarkdown | What Lensdrop is and how delivery works | text/markdown |
GET /pricing.md | getPricingMarkdown | Lensdrop pricing: tiers, add-ons, free tier, billing | text/markdown |
GET /download.md | getDownloadMarkdown | Lensdrop desktop uploader | text/markdown |
GET /help.md | getHelpMarkdown | Lensdrop help and FAQ | text/markdown |
GET /use-cases.md | getUseCasesMarkdown | Lensdrop use cases, all niches in one document | text/markdown |
GET /blog.md | getBlogIndexMarkdown | Lensdrop blog index | text/markdown |
GET /developers.md | getDeveloperDocsMarkdown | Lensdrop developer docs | text/markdown |
GET /use-cases/{slug}.md | getUseCaseMarkdown | One use case as markdown | text/markdown |
GET /blog/{slug}.md | getBlogPostMarkdown | One blog post as markdown | text/markdown |
GET /openapi.json | getOpenApiSpec | OpenAPI 3.1 description of this API | application/openapi+json |
GET /.well-known/api-catalog | getApiCatalog | RFC 9727 API catalog | application/linkset+json |
GET /auth.md | getAuthMd | How agents authenticate (they don't) | text/markdown |
Errors
Every error is an RFC 9457 problem details object served as application/problem+json, with a real HTTP status: never a 200 wrapping an error, never an HTML page for an API path. Fields: type (documentation URL for the code), title, status, code (stable, machine-readable), detail, hint (what to do next), and optional instance (the request path). error is a legacy alias of detail.
{
"type": "https://lensdrop.app/developers#not_found",
"title": "Not found",
"status": 404,
"code": "not_found",
"detail": "No markdown twin at /blog/no-such-post.md.",
"hint": "Start from /llms.txt for the agent-readable site index, /openapi.json for the API description, or /sitemap.xml for every page.",
"instance": "/blog/no-such-post.md",
"error": "No markdown twin at /blog/no-such-post.md."
}| Status | Code | Title | What to do |
|---|---|---|---|
| 400 | bad_request | Bad request | Check the request body and parameters against the documented schema. |
| 400 | unsupported_version | Unsupported API version | Send a supported value in the API-Version header, or omit the header to use the current version. |
| 401 | unauthorized | Unauthorized | The credential is missing, malformed, or revoked. The public content API needs no credential; other surfaces are not open to third parties. |
| 403 | forbidden | Forbidden | The credential is valid but does not cover this resource. |
| 404 | not_found | Not found | Start from /llms.txt for the agent-readable site index, /openapi.json for the API description, or /sitemap.xml for every page. |
| 405 | method_not_allowed | Method not allowed | The content API is read-only: use GET or HEAD. |
| 406 | not_acceptable | Not acceptable | This resource is served as text/markdown or application/json. Adjust the Accept header. |
| 409 | conflict | Conflict | The resource is in a state that does not allow this change. Re-read it and retry if appropriate. |
| 413 | payload_too_large | Payload too large | Reduce the request size; uploads go directly to storage in parts, never through this API. |
| 429 | rate_limited | Too many requests | Wait for the number of seconds in Retry-After, then retry. The RateLimit and RateLimit-Policy headers describe the quota. |
| 500 | internal_error | Internal error | Retry with backoff. If it persists, contact support with the instance path. |
| 502 | bad_gateway | Upstream error | A dependency failed. Retry with backoff. |
| 503 | service_unavailable | Service unavailable | Retry after a short delay; honor Retry-After when present. |
Unknown paths anywhere on the site return a real 404. Browsers get the HTML page, /api/* paths and JSON accepts get a problem document, and everything else (curl, agents) gets a markdown body with links to the discovery files above.
Rate limits
Anonymous access is limited per client IP to 120 requests per 60 seconds (sliding window), described by the IETF RateLimit header fields on every response. Exceeding it returns 429 rate_limited with Retry-After. Read RateLimit to self-throttle instead of retrying blindly.
| Header | Meaning | Example |
|---|---|---|
API-Version | The API version that produced the response. Currently 1. | 1 |
RateLimit-Policy | The quota policy (IETF RateLimit header fields): 120 requests per 60 seconds per client, sliding window. | "content-api";q=120;w=60 |
RateLimit | Live quota state: r is the requests remaining in the window, t the seconds until it resets. Present whenever the limiter ran. | "content-api";r=119;t=60 |
Retry-After | On a 429 only: seconds to wait before retrying. | 12 |
Deprecation | Only on responses from a deprecated API version (RFC 9745): the date the deprecation was announced. | @1767225600 |
Sunset | Only on responses from a deprecated API version (RFC 8594): the date it stops being served. | Wed, 01 Jul 2026 00:00:00 GMT |
Versioning and deprecation
The content API is versioned by the API-Version header. Every response states the version that produced it (currently 1). Requests may pin a version with the same header; omit it to get the current version. A pinned version this deployment no longer serves answers 400 with code unsupported_version and the supported list.
URLs and response shapes within a version only change additively: new fields, new endpoints, new headers. Removing or renaming anything means a new version.
When a version is deprecated, responses from it carry Deprecation (RFC 9745) and Sunset (RFC 8594) headers and a Link with rel="deprecation" pointing at the notice on this page, for at least 6 months before the sunset date. Deprecations are also listed in llms.txt.
No version has been deprecated yet.
What is not public
- Gallery uploads, downloads, and the client viewer: no third-party API. Bytes move between browsers, storage, and an image worker through signed URLs, never through an app server that an integration could call.
- Billing: photographers pay Lensdrop through Dodo Payments, the merchant of record for both currencies (INR in India, USD elsewhere). There is no API to create purchases, and there are never client-to-photographer payments.
- Connected devices: the desktop uploader and camera FTP use per-device credentials that the app issues and can revoke. They are not API keys for general use and are not documented here.
Crawling and AI use
robots.txt allows search engines and AI crawlers by name (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, and others) on the public pages and publishes Content Signals permitting search, AI input, and AI training. Galleries (/g/), the signed-in app, and /api are not crawlable.
Contact
Questions about agent access or this API: hello@lensdrop.app. Product questions belong in the help center.
