Location Sharing API
Request, read, and watch Find My location shares via the Sendblue API
Request and read Find My location shares from your recipients. Location Sharing lets your line ask a contact to share their location over iMessage, read the location of contacts already sharing with your line, and stream live location updates as they move.
Location Sharing is available on dedicated Sendblue lines with iMessage enabled, and only works with iMessage recipients.
There are three capabilities:
| Capability | Endpoint | Description |
|---|---|---|
| Request | POST /api/request-location | Send a Find My location-request card to a contact. When they accept, the shared location is delivered to your receive webhook. |
| Read | GET /api/location · GET /api/location/{number} | Read the current location of contacts already sharing with your line. |
| Watch | GET /api/location/{number}/watch | Stream live location updates for a contact over Server-Sent Events (SSE). |
A contact must be sharing their location with your line before Read and Watch return data. Use Request to prompt a contact who is not sharing yet — once they accept, they become a sharing contact for the duration they selected.
Request a Location Share
Section titled “Request a Location Share”Send a Find My location-request card to a contact. This appears in their iMessage conversation as a native “Share My Location” request that they can accept and choose a duration for.
POST https://api.sendblue.com/api/request-locationRequest Body
Section titled “Request Body”| Parameter | Type | Required | Description |
|---|---|---|---|
number | string | Yes | Recipient phone number (E.164 format) |
from_number | string | Yes | Your Sendblue line (E.164 format) |
Example Request
Section titled “Example Request”curl -X POST 'https://api.sendblue.com/api/request-location' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "number": "+19175551234", "from_number": "+14155559876" }'Node.js Example
Section titled “Node.js Example”const axios = require('axios');
await axios.post( 'https://api.sendblue.com/api/request-location', { number: '+19175551234', from_number: '+14155559876' }, { headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' } });Success Response (202)
Section titled “Success Response (202)”{ "status": "QUEUED", "message": "Location request accepted for dispatch", "number": "+19175551234", "message_handle": "6f1b2c3d4e5f6789", "uuid": "6f1b2c3d4e5f6789"}The request is queued and sent to your line. The recipient sees a location-request card in iMessage. The response does not contain a location — the shared location is delivered later, via webhook, once the recipient accepts.
Receiving the shared location
Section titled “Receiving the shared location”When the recipient accepts the request, Sendblue POSTs an inbound message to your configured receive webhook. It’s a standard inbound message webhook with two additions for location shares: message_type is "location", and a location object is included.
{ "is_outbound": false, "message_handle": "99DCC379-DD76-4712-BA65-11EFB33B8CD6", "from_number": "+19175551234", "message_type": "location", "location": { "latitude": 37.331413, "longitude": -122.030731, "accuracy": 12.5, "altitude": 8.2, "timestamp": "2025-12-12T15:41:20.000Z", "duration": "endOfDay" }}The standard message fields (number, to_number, sendblue_number, content, media_url, and so on) are omitted above for brevity — see the inbound message webhook reference for the full payload.
location object
Section titled “location object”| Field | Type | Description |
|---|---|---|
latitude | number | Latitude in decimal degrees |
longitude | number | Longitude in decimal degrees |
accuracy | number | undefined | Horizontal accuracy in meters, when available |
altitude | number | undefined | Altitude in meters, when available |
timestamp | string | undefined | ISO 8601 time the fix was captured |
duration | string | undefined | The share duration the recipient selected, as an Apple-defined key (e.g. oneHour, endOfDay, indefinitely) |
Read Current Shared Location
Section titled “Read Current Shared Location”Read the current location of contacts already sharing with your line. These endpoints return the latest cached fix — they do not send a request card. A contact must already be sharing (for example, after accepting a location request).
List all sharing contacts
Section titled “List all sharing contacts”GET https://api.sendblue.com/api/locationQuery Parameters
Section titled “Query Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
from_number | string | Yes | Your Sendblue line (E.164 format) |
Example Request
Section titled “Example Request”curl -X GET 'https://api.sendblue.com/api/location?from_number=%2B14155559876' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET'Success Response (200)
Section titled “Success Response (200)”{ "status": "OK", "from_number": "+14155559876", "locations": [ { "number": "+19175551234", "state": "shared_with_fix", "location": { "address": "1 Apple Park Way, Cupertino, CA", "latitude": 37.331413, "longitude": -122.030731, "accuracy": 12.5, "altitude": 8.2, "timestamp": "2025-12-12T15:41:20.000Z", "locationType": "live", "expiresAt": "2025-12-12T23:59:59.000Z" } } ]}Get one contact’s location
Section titled “Get one contact’s location”GET https://api.sendblue.com/api/location/{number}The {number} path parameter is the recipient phone number in E.164 format (URL-encode the leading + as %2B).
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
from_number | string | Yes | Your Sendblue line (E.164 format) |
Example Request
Section titled “Example Request”curl -X GET 'https://api.sendblue.com/api/location/%2B19175551234?from_number=%2B14155559876' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET'Success Response (200)
Section titled “Success Response (200)”{ "status": "OK", "from_number": "+14155559876", "number": "+19175551234", "state": "shared_with_fix", "location": { "address": "1 Apple Park Way, Cupertino, CA", "latitude": 37.331413, "longitude": -122.030731, "accuracy": 12.5, "altitude": 8.2, "timestamp": "2025-12-12T15:41:20.000Z", "locationType": "live", "expiresAt": "2025-12-12T23:59:59.000Z" }}If the contact is not sharing with your line, the response has state: "not_shared" and no location object.
Read response schema
Section titled “Read response schema”| Field | Type | Description |
|---|---|---|
number | string | null | The contact’s phone number |
state | string | One of not_shared, shared_no_fix_yet, or shared_with_fix (see below) |
location | object | Present when a fix is available (omitted for not_shared) |
state values
Section titled “state values”| Value | Meaning |
|---|---|
not_shared | The contact is not sharing their location with your line |
shared_no_fix_yet | The contact is sharing, but no coordinates are available yet |
shared_with_fix | The contact is sharing and coordinates are available in location |
location object
Section titled “location object”| Field | Type | Description |
|---|---|---|
address | string | null | Reverse-geocoded address, when available |
latitude | number | Latitude in decimal degrees (omitted for a placeholder 0,0 fix) |
longitude | number | Longitude in decimal degrees (omitted for a placeholder 0,0 fix) |
accuracy | number | Horizontal accuracy in meters, when available |
altitude | number | Altitude in meters, when available |
timestamp | string | ISO 8601 time the fix was captured |
locationType | string | One of live, shallow, legacy, or unknown |
expiresAt | string | ISO 8601 time the share expires, when known |
Watch Live Location
Section titled “Watch Live Location”Stream live location updates for a single contact over Server-Sent Events (SSE). The connection stays open and emits a location event each time a new fix arrives, until you close it, the contact stops sharing, or the line disconnects.
GET https://api.sendblue.com/api/location/{number}/watchThe {number} path parameter is the recipient phone number in E.164 format (URL-encode the leading + as %2B). The contact must already be sharing with your line.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
from_number | string | Yes | Your Sendblue line (E.164 format) |
Example Request
Section titled “Example Request”curl -N 'https://api.sendblue.com/api/location/%2B19175551234/watch?from_number=%2B14155559876' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Accept: text/event-stream'The -N (--no-buffer) flag keeps curl from buffering the stream so events print as they arrive.
Event Stream
Section titled “Event Stream”The server responds with Content-Type: text/event-stream and emits named events:
event: readydata: {"status":"OK","from_number":"+14155559876","number":"+19175551234"}
event: locationdata: {"from_number":"+14155559876","number":"+19175551234","state":"shared_with_fix","location":{"latitude":37.331413,"longitude":-122.030731,"accuracy":12.5,"altitude":8.2,"timestamp":"2025-12-12T15:41:20.000Z","locationType":"live"}}
event: completedata: {"reason":"sharing_ended"}| Event | Description |
|---|---|
ready | The watch is established. Sent once, before the first fix. |
location | A new location fix. The data payload matches the read response (number, state, and an optional location object). |
error | The watch failed. The data payload contains a message. The stream then ends. |
complete | The watch ended normally. The data payload contains a reason such as sharing_ended, authorization_revoked, or worker_disconnected. |
The stream also sends SSE comment lines (: keepalive) roughly every 15 seconds to keep the connection alive. Ignore any line beginning with :.
Node.js Example
Section titled “Node.js Example”// Uses the built-in fetch (Node 18+) to read the SSE stream.const res = await fetch( 'https://api.sendblue.com/api/location/%2B19175551234/watch?from_number=%2B14155559876', { headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', Accept: 'text/event-stream' } });
const reader = res.body.getReader();const decoder = new TextDecoder();let buffer = '';
while (true) { const { value, done } = await reader.read(); if (done) break;
buffer += decoder.decode(value, { stream: true }); const chunks = buffer.split('\n\n'); buffer = chunks.pop() ?? '';
for (const chunk of chunks) { const eventLine = chunk.split('\n').find((l) => l.startsWith('event:')); const dataLine = chunk.split('\n').find((l) => l.startsWith('data:')); if (!eventLine || !dataLine) continue; // skip keepalive comments
const event = eventLine.slice('event:'.length).trim(); const data = JSON.parse(dataLine.slice('data:'.length).trim());
if (event === 'location') { console.log('New fix:', data.location); } else if (event === 'complete' || event === 'error') { break; } }}To stop watching, close the HTTP connection.
Error Responses
Section titled “Error Responses”| HTTP Status | Message / message field | Description |
|---|---|---|
| 400 | You must specify a valid from_number... | Missing or invalid number / from_number |
| 400 | Your account has been blocked... | The account is blocked |
| 401 | Unauthorized | Invalid or missing API credentials |
| 403 | Location request blocked by account policy | The request was declined by account send policy (e.g. rate/line limits) |
| 404 | The from_number is not registered | The from_number is not a line on your account |
| 422 | unsupported_on_this_line | This line doesn’t support Location Sharing — it isn’t a dedicated iMessage line, is a shared line, or doesn’t have iMessage enabled |
| 422 | unsupported_recipient | The recipient is known to be SMS/RCS; Find My requires an iMessage recipient |
| 503 | worker_unavailable | The line is not currently connected |
| 503 | worker_version_unsupported | This line doesn’t support Location Sharing yet; try again shortly |
| 504 | Timeout waiting for location read | A read did not complete in time; retry |
{ "status": "ERROR", "message": "Description of what went wrong"}Some 422 responses include a detail field with a human-readable explanation.
-
Line support: Location Sharing requires a dedicated Sendblue line with iMessage enabled. It isn’t available on shared lines, and some lines may not support it — those return a
422or503error. -
iMessage only: Find My location sharing is an iMessage feature. Requests to numbers known to be SMS/RCS are rejected with
unsupported_recipient. Unknown numbers are allowed and resolved as iMessage on send. -
Request is asynchronous:
POST /api/request-locationonly sends the request card. The location arrives later, via yourreceivewebhook, once the recipient accepts — and only if they accept. -
Read/Watch need an active share:
GET /api/location*and the watch endpoint only return data for contacts already sharing with your line. Use a location request to prompt a contact who is not sharing yet. -
Shares expire: Recipients choose how long to share (for example, one hour, until end of day, or indefinitely). Once a share expires, the contact reverts to
not_sharedand watches for them end with acompleteevent. -
Requests count toward your limits: A location request counts as a message and is subject to your account’s sending limits.