Connect a website, script or agent to your private photo library through URSY ID. The website and API use the same library and storage allowance.
ID login and account creation · Typed OpenAPI · Capabilities · Developer manifest · Connected apps
Base URL: https://ursy.photos/api/v1. Resource: https://ursy.photos/api. Send an access token in the Authorization: Bearer header. Request only the permissions needed.
GET /photos?limit=50 returns next_cursor, library_revision and sync_cursor. Follow the opaque cursor with the same filters. The cursor fixes an upload high-water mark, so concurrent uploads cannot shift or duplicate the remaining pages. Existing records can change; use the changes feed after the listing. Photo cursors expire after one hour. The legacy after parameter remains supported. Albums expose next_after.
Poll GET /changes?cursor=... with the sync cursor. Changes and deletion tombstones are retained for 30 days. CURSOR_EXPIRED requires a fresh listing. A narrowed selection invalidates cursors for the previous selection.
Create an upload with POST /upload/init and JSON name, size, mime. For a selected-album grant, include its album_id. Upload raw chunks to POST /upload/chunk?upload_id=...&offset=..., at most 2 MiB each. Resume with GET /upload/{upload_id}. Finish with JSON upload_id, sha256; only a verified receipt confirms storage. Sessions expire after 24 hours. An upload belongs to its initiating grant.
For bytes, request POST /photos/{id}/asset-grants with size: "md" or "original". URLs expire within five minutes and recheck the grant. Originals preserve the uploaded bytes and require both photos.originals.read and photos.location.read. Previews contain no embedded EXIF. Image limits are 25 MiB, 24 megapixels and 16,000 pixels per edge; check capabilities for the server’s current HEIC support.
During ID authorization, use authorization_details containing one ursy_photos_access object with album_ids and/or photo_ids. The same limits apply to lists, media, uploads, changes, sharing and exports.
POST /shares/plan prepares an exact photo selection and returns an ID approval_url. Show the plan to the owner and let them approve it at ID. Only then send plan_id, plan_hash to POST /shares/apply. A changed library or selection returns PLAN_STALE. Shared selections are frozen and contain previews only. Adding a photo to the source album does not silently publish it.
POST /exports queues a ZIP containing unchanged originals, metadata, album memberships and checksums. Supply an Idempotency-Key or JSON operation_id. Optional selection fields narrow the export. Poll GET /tasks/{id} and cancel using DELETE /tasks/{id}. Completed tasks offer POST /tasks/{id}/download-grant for a five-minute download URL. Task records and artifacts expire after 24 hours. Permission or library changes stop an export or invalidate its artifact; create a fresh export.
POST JSON-RPC to https://ursy.photos/api/mcp. server/discover publishes current versions and the Tasks extension. The current protocol uses per-request _meta and matching Mcp-Method/Mcp-Name headers. Clients declaring io.modelcontextprotocol/tasks get durable export task handles and poll tasks/get; tasks/cancel requests cancellation. Existing 2025 clients can use initialization and ordinary task tools. Notifications and subscriptions are not offered.
GET /photosGET /photos/searchGET /photos/{id}GET /quotaGET /mePATCH /photos/{id}POST /photos/{id}/asset-grantsGET /albumsPOST /albumsGET /albums/{id}PATCH /albums/{id}DELETE /albums/{id}POST /albums/{id}/photosDELETE /albums/{id}/photosGET /albums/{id}/photosPOST /upload/initPOST /upload/finishGET /upload/{upload_id}DELETE /upload/{upload_id}POST /upload/chunkDELETE /photo/{id}POST /photo/{id}/restoreGET /trashGET /timelineGET /stackGET /changesGET /activityGET /duplicatesGET /library/statusGET /sharesPOST /shares/planGET /shares/plans/{id}POST /shares/applyDELETE /shares/{id}POST /sharesPOST /exportsGET /tasksGET /tasks/{id}DELETE /tasks/{id}POST /tasks/{id}/download-grantErrors carry error.code, a message and a request ID. The legacy code, message, data.status fields remain available. Refresh authentication after a 401, request the missing permissions after a 403, and follow Retry-After on a 429. Private results never contain sponsorship. Paid checkout, Family accounts, semantic search and automatic camera-roll backup are outside this website release.