update_media

Set a photo's caption, alt, credit, focal point or provenance.

PATCH/v1/media/{mediaId}
MCP tool update_mediaScope: writeStableSince 2026-09-17
Markdown

Credits render as Photo: Name and are a hard platform rule: never drop or invent one; ask the creator whose photo it is. Only sent fields change; null clears a metadata field (alt, focal point, licence, shows, takenAt and the rest), while caption and credit take text and store it as sent. The response carries warnings that did not stop the write.

curl -X PATCH "https://sceniq.earth/api/v1/media/kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "alt": "A deep blue pool ringed with orange and yellow, steam drifting over the boardwalk below",
  "focalX": 0.52,
  "focalY": 0.46,
  "shows": {
    "kind": "spot"
  },
  "takenAt": {
    "lat": 44.5199,
    "lon": -110.8406,
    "precision": "gps"
  },
  "locationVerifiedBy": "geotag"
}'

Path parameters

  • mediaIdstring · photo idrequired

    Id of the media row.

Body parameters

  • altstring or nulloptional

    Alt text: what the photo shows, for screen readers. The caption is the fallback.

  • captionstringoptional

    Caption text, shown under the photo. Stored as sent.

  • capturedOnstring or nulloptional

    Capture date (YYYY-MM-DD). Upload reads it from the file's metadata before stripping it when present.

  • creditstringoptional

    Photographer credit, rendered as Photo: Name. Stored as sent.

  • creditUrlstring or nulloptional

    The photographer's profile page.

  • focalXnumber or nulloptional

    Focal point 0 (left) to 1 (right); with focalY the apps keep it in frame when they crop to a card.

  • focalYnumber or nulloptional

    Focal point 0 (top) to 1 (bottom).

  • licencestring or nulloptional

    Licence name (Unsplash License, CC BY 4.0).

  • locationVerifiedBystring or nulloptional

    How the location was confirmed: geotag, landmark, photographer_caption, creator or other.

    geotaglandmarkphotographer_captioncreatorother
  • reviewNotesstring or nulloptional

    Notes for the creator's review (why a value was chosen, what could not be verified). Never shown to buyers; get_guide_readiness lists them. Empty string clears.

  • showsPhotoShows or nulloptional

    What the photo shows, null clears: { kind: spot|view_from_spot|nearby|approach|detail|other, label? }.

    Show 2 fieldsof PhotoShows
    • kindstringrequired

      spot (the place itself), view_from_spot (the view from it), nearby (a sight near the spot; readiness then skips its distance check), approach (the way there), detail or other.

      spotview_from_spotnearbyapproachdetailother
    • labelstringoptional

      What exactly it shows, at most 80 characters (the north face from the lake).

  • sourcePageUrlstring or nulloptional

    The page the photo was licensed from (an Unsplash photo page). Not the image file.

  • takenAtPhotoTakenAt or nulloptional

    Where it was taken, null clears: { lat, lon, precision: gps|geocode|none }. A gps fix more than 2 km from the pin is flagged by readiness.

    Show 3 fieldsof PhotoTakenAt
    • latnumberrequired

      Latitude in decimal degrees (-90 to 90).

    • lonnumberrequired

      Longitude in decimal degrees (-180 to 180).

    • precisionstringrequired

      gps (a camera or phone fix), geocode (looked up from a place name, often coarse on stock sites) or none (an estimate; readiness lists it as resting on judgement).

      gpsgeocodenone

Returns

200 OK with this object.

  • okbooleanrequired

    Always true: the write went through.

    Always true

  • mediaIdstring · photo idrequired

    The photo's id.

  • warningsarray of stringsoptional

    Notes about things the write accepted but you should look at (a plain http link). Present only when there are any.

Response 200
{
  "ok": true,
  "mediaId": "kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9"
}

Errors

Every error is { error: { code, message } }. Act on the code.

  • 400invalid_argumentA required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
  • 400invalid_requestA writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
  • 401unauthenticatedThe request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
  • 401invalid_api_keyThe key is unknown, revoked or expired.
  • 403api_scope_requiredA read-only key called a write operation, or a key without the publish option called publish_changes.
  • 403forbiddenThe key belongs to a creator account that is no longer active.
  • 404not_foundThe id does not exist or belongs to another creator. The two answers are identical on purpose, so ids never reveal what exists.
  • 413payload_too_largeA JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
  • 429rate_limitedOver a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
  • 500internal_errorAn unexpected failure. The message is hidden on purpose.