# Lookups

> Ask whether a person uses iMessage. The answer is a best guess from your own Messages history and says what it’s based on. This page also covers when a lookup helps and when it doesn’t.

`GET /v1/lookup` answers “is this person on iMessage?” as well as anyone outside Apple can: by reading what the newest chat with them in your own Messages used. Apple offers no way to ask about somebody you’ve never texted, so for them the answer is `unknown`, and the answer always says what it rests on.

```bash
curl "https://usebluebot.com/api/v1/lookup?number=%2B15555550123" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const lookup = await client.lookup('+15555550123');
console.log(lookup);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

lookup = client.lookup("+15555550123")
print(lookup)
```

The answer, `200`:

```json
{
  "object": "lookup",
  "handle": "+15555550123",
  "service": "imessage",
  "basis": "history",
  "last_seen_at": "2026-11-12T16:20:00.000Z",
  "checked_at": "2026-11-13T19:41:00.000Z"
}
```

`number` takes a phone number or an email address, written any way `to` takes one on a send. Write the `+` as `%2B` in a URL you build by hand; the SDKs and the request examples here do it for you. A value BlueBot can’t read as either is `400 invalid_request`.

## What the answer means

`service` is the guess and `basis` is what it rests on:

| `service`  | `basis`          | It means                                                                                                                 |
| ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `imessage` | `history`        | The newest chat with this person in your Messages is an iMessage chat. They used iMessage as of `last_seen_at`.          |
| `sms`      | `history`        | The newest chat is a regular text (SMS, MMS or RCS) chat. They may have no iPhone, or they may have turned iMessage off. |
| `unknown`  | `none`           | You’ve never had a chat with this person, so there’s nothing to go on.                                                   |
| `unknown`  | `device_offline` | Your Mac wasn’t online to ask.                                                                                           |
| `unknown`  | `timeout`        | Your Mac didn’t answer within 15 seconds.                                                                                |

`history` is good evidence and nothing more. People switch phones, and somebody whose newest chat with you was over iMessage a year ago may be on Android now. Look at `last_seen_at`: an answer from last week is worth more than one from last year.

| You got                     | It means                                     | Do next                                                                                             |
| --------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `"service": "imessage"`     | They used iMessage in your newest chat.      | Send with `auto`, or `imessage` if the text only makes sense as an iMessage.                        |
| `"service": "sms"`          | Your newest chat with them is regular texts. | Send with `auto`. Keep files small; regular texts carry less.                                       |
| `"basis": "none"`           | Nothing to go on.                            | Send with `auto`. It tries iMessage first and falls back to a regular text once.                    |
| `"basis": "device_offline"` | The Mac is away.                             | Wake the Mac, or send anyway: a text waits for it.                                                  |
| `"basis": "timeout"`        | The Mac didn’t answer in time.               | Try again in a minute. If it keeps happening, check the Mac’s Full Disk Access in BlueBot’s window. |

## How a lookup works

Your Mac answers it. BlueBot asks at the Mac’s next check-in, the Mac reads your Messages history for the newest chat with that person, and BlueBot holds your request open for up to 15 seconds to hear back. Most lookups take one or two check-ins, a few seconds. If no answer comes in time, the API answers `unknown` with `timeout` rather than making you wait longer.

The Mac needs Full Disk Access to read Messages’ history. Without it the Mac can’t answer any lookup, and every lookup times out. BlueBot’s window on the Mac shows the check and how to fix it, and `GET /v1/device` shows it as the `full_disk_access` check.

An answer is reused for 10 minutes. Asking about the same person twice in a row costs one trip to the Mac, and the second answer’s `checked_at` is the first one’s.

## When to use it

- **Showing it to a person.** A blue or green badge next to a contact in your CRM tells your team how a text will arrive.
- **Before an iMessage-only send.** If a text only works as an iMessage (a long video, a group plan), look up first and send `service: "imessage"` only to people whose answer is `imessage`. Everyone else gets a different text, or none.
- **Sorting a list once.** Before a campaign, look up each person once and keep the answers, rather than asking again for every send.

## When not to

- **Before every send.** `service: "auto"` already uses the newest chat’s service at the moment the Mac sends, and falls back from iMessage to a regular text once on its own. A lookup first adds up to 15 seconds and a request for no better result.
- **To find out about a stranger.** For anybody you’ve never texted, the answer is `unknown`. Nothing in BlueBot, or anywhere outside Apple, can say whether a number you’ve never texted is on iMessage.
- **In a tight loop.** Each lookup can hold a request for 15 seconds and counts against your 120 requests a minute. Look up a few at a time, not hundreds at once. [Rate limits](https://usebluebot.com/docs/concepts/rate-limits)

## In the SDKs

```ts title="lookup.ts"
const lookup = await client.lookup('+15555550123');
if (lookup.service === 'imessage') {
    // Safe to send the iMessage-only version.
}
```

```python title="lookup.py"
lookup = client.lookup("+15555550123")
if lookup.service == "imessage":
    ...  # Safe to send the iMessage-only version.
```

The Zapier app has the same check as its **Check iMessage** search, and the MCP server as its `lookup_service` tool, so an assistant can ask before it sends. [Services and SMS fallback](https://usebluebot.com/docs/concepts/services) explains how BlueBot chooses between iMessage and a regular text on every send.
