Exporting Your Conversation Data (Data API)
The Maya Data API gives you every conversation your visitors had with Maya: each message, and what Maya made of the conversation. Use it to load your conversations into your own database, warehouse or BI tool, and build your reports there.
What It Is
The Data API is a read-only web address that a script or data tool calls to fetch your conversations. Each conversation comes as one record with its messages inside it, in JSON, with every time in UTC.
It is meant for your data team, your BI tool, or an agency that builds reports for you. It only ever reads your own organisation's conversations, and it changes nothing in Maya.
The address everything starts from is the API base URL:
https://api.mayatravel.ai/v1
Every field it returns, and the values those fields can have, is listed in the Data API reference.
Creating an API Key
You need an admin login for Maya Insights. The API keys card creates read-only conversation-export keys; there is no permission selector. To connect an AI assistant instead, use the separate AI Connections (MCP) card and follow https://docs.mayatravel.ai/mcp.
- In the Maya Insights dashboard, go to Connections, then the API keys card, then New API key.
- Type a Name you will recognise later, such as the agency or tool the key is for. It is how you tell keys apart when you revoke one.
- Click Create. The key is shown once and never again. Copy it, together with the API base URL shown next to it, and give both to whoever loads the data. Share the key somewhere private, not by email.
Create a separate key for each tool or agency. You can then revoke one without affecting the others, and revoking takes effect on the next request.
Create the key from an admin login that will stay with your organisation. A key stops working when the person who created it loses access to your organisation in Maya Insights, so a key made by someone who later leaves stops working without warning.
Check That It Works
Ask the API who the key belongs to. Replace YOUR-KEY with the key you copied:
curl -sS https://api.mayatravel.ai/v1/me -H "Authorization: Bearer YOUR-KEY"
The answer names your organisation and the limits that apply:
{
"organization": "example-travel",
"scopes": ["maya:export"],
"limits": { "conversations_per_minute": 500, "concurrent_requests": 1, "page_size": 50 }
}
Keeping Your Own Copy
Keep your own copy of the conversations, in a database or warehouse, and build your reports on that copy. You can then query it as often as you like, without limits. The API is for filling and refreshing that copy, not for running reports against directly.
Making the First Copy
- Note the current time.
- Call
/conversationswithout filters. Save every conversation on the page, then call it again with thenext_cursorfrom the answer. Keep going untilhas_moreisfalse. - Store the time you noted. The next step starts from it.
A large history takes a while: about two hours per 60,000 conversations, because of the limit of 500 conversations a minute.
Keeping It Up to Date
- On a schedule, anywhere from every 15 minutes to once a day, call
/conversationswithupdated_afterset to the latestupdated_at_utcyou have stored. The first time, use the time you noted before making the first copy. - Insert or update each conversation by its
chat_uuid, and replace its messages with the ones you just received. Receiving the same conversation twice is harmless.
A conversation shows up in these results about 30 minutes after it last changed, once Maya has finished processing it, so its title, outcome, sentiment, topics and places are filled in. If the visitor comes back later, the conversation changes again and comes along in your next run.
Refreshing It Now and Then
A few changes don't come along with updated_after. Conversations that are deleted or marked as a test later stay in your copy, because the API doesn't report removals. Details Maya adds well after a conversation went quiet, and new messages in conversations on WhatsApp, Instagram or Facebook, can also be missed.
Make the copy again from scratch now and then, for example once a month, and replace your old copy with it. Do the same if you ever lose your copy.
An Example in Python
This does both steps. Fill in save, store_since and load_since with your own database calls:
import time, requests
BASE = "https://api.mayatravel.ai/v1"
HEADERS = {"Authorization": "Bearer YOUR-KEY"}
def conversations(params=None):
params = dict(params or {})
while True:
r = requests.get(f"{BASE}/conversations", headers=HEADERS, params=params)
if r.status_code == 429: # limit reached: wait and retry
time.sleep(int(r.headers.get("Retry-After", "10")))
continue
r.raise_for_status()
page = r.json()
yield from page["data"]
if not page["has_more"]:
return
params = {"cursor": page["next_cursor"]} # the cursor remembers your filters
# the first copy, once
since = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
for conversation in conversations():
save(conversation) # your own insert-or-update
store_since(since)
# keeping it up to date, on a schedule
since = load_since()
for conversation in conversations({"updated_after": since}):
save(conversation)
since = conversation["updated_at_utc"] # results come oldest change first
store_since(since)
Tools that sync from an API, such as Airbyte or dlt, can do the same. Point them at /conversations, page with next_cursor until has_more is false, and use updated_at_utc as the incremental field. Never stop on an empty page. Keep going while has_more is true: a page can hold fewer than 50 conversations, or none, and still have more after it.
Limits
Your organisation can read up to 500 conversations a minute. A page of 50 counts as 50, so that is about 10 pages a minute.
Requests run one at a time for your organisation. If you send several at once, the extra ones are refused with a 429, so send them one after another.
Every answer says where you stand, in two headers: RateLimit-Limit and RateLimit-Remaining. When you are over the limit, the answer is a 429 with Retry-After, the number of seconds to wait before you continue with the same cursor.
When It Does Not Work
Errors come back as JSON with a code you can check and a message you can read:
{ "error": { "code": "rate_limited", "message": "Limit of 500 conversations per minute reached. Try again in 12 seconds." } }
Every call answers 401. The key is missing, mistyped or revoked. Check that it is sent as Authorization: Bearer followed by the key, or ask your admin for a new one.
Every call answers 403. The key doesn't have the Export conversations permission, for example because it was made for an AI tool. Or the person who created it no longer has access to your organisation. Create a new key with Export conversations, as described above.
Calls answer 429. You are over the 500 conversations a minute, or another request for your organisation is still running. Wait the seconds in Retry-After, then continue with the same cursor. If you run several jobs, make sure only one calls the API at a time.
A cursor is refused with 400. The cursor is damaged, or you sent it with filters that differ from the first page's. Send the cursor alone, or with exactly the same filters. If it is still refused, start that run again without a cursor.
A recent conversation is missing from your copy. It changed less than 30 minutes ago, so it comes along in your next run.
A deleted conversation is still in your copy. The API doesn't report removals. Make the copy again from scratch, as described in Refreshing It Now and Then.
An answer is 500 or 503. Something went wrong on the Maya side. Wait a short while and retry with the same cursor. Nothing is lost.
Personal Data
Messages come exactly as your visitors typed them, so they can contain names, email addresses or phone numbers. When a visitor leaves an email address in the chat, left_contact_details is true; the details themselves are only in the message text. Store and share the data according to your own privacy policy.