---
title: "Quickstart"
description: "Create an API key, call GET /v1/me, list your listings and add a contact with curl, JavaScript or Python. The Matchai API is coming soon."
last_updated: "2026-10-21"
lang: "en"
status: "coming-soon"
canonical: "https://matchai.co/docs/developers/quickstart"
---

# Quickstart

> Create an API key, call GET /v1/me, list your listings and add a contact with curl, JavaScript or Python. The Matchai API is coming soon.

**Coming soon. This is documented ahead of launch so you can plan your integration; it is not available yet.**

Source: https://matchai.co/docs/developers/quickstart

This quickstart takes you from a new API key to your first contact in four calls. It works on your own Matchai account, so there is no app to register and nothing to build first.

<Callout title="You cannot run these calls yet">
The Matchai API is coming soon. The requests below show how it will work when it opens, so you can plan your integration. Until then, build on the [public surfaces](https://matchai.co/docs/developers#what-is-live-today).
</Callout>

## Before you start [#before-you-start]

- A Matchai account for a real estate professional. Everything you call acts for that one account.
- A way to run a request: `curl`, Node.js 20 or later, or Python 3 with the `requests` package.

## Make your first calls [#make-your-first-calls]

<Steps>
<Step>

**Create an API key.**

Open **Settings → Connectors**, expand **Advanced (for developers)** and go to **API keys**. Create a key, name it (for example "Quickstart") and give it three scopes: `profile:read`, `listings:read` and `contacts:write`.

Copy the key straight away. Matchai shows it once. It starts with `matchai_`. Keep it in an environment variable, never in your code:

```bash title="Terminal"
export MATCHAI_API_KEY="matchai_…"
```

Every key belongs to your account's **API access** connection, which appears on the Connectors page like any other connection. See [API keys](https://matchai.co/docs/developers/authentication/api-keys) for scopes, rotation and revocation.

</Step>
<Step>

**Check which account the key reaches.** `GET /v1/me` returns the account your key acts for.

<Tabs items={['curl', 'JavaScript', 'Python']}>
<Tab value="curl">

```bash title="Terminal"
curl https://api.matchai.co/v1/me \
  -H "Authorization: Bearer $MATCHAI_API_KEY"
```

</Tab>
<Tab value="JavaScript">

```js title="me.mjs"
const response = await fetch('https://api.matchai.co/v1/me', {
  headers: { Authorization: `Bearer ${process.env.MATCHAI_API_KEY}` },
});

if (!response.ok) {
  const { error } = await response.json();
  const requestId = response.headers.get('Matchai-Request-Id');
  throw new Error(`${error.code}: ${error.message} (request ${requestId})`);
}

const me = await response.json();
console.log(`Connected as ${me.display_name}. Energy: ${me.energy.balance_state}.`);
```

</Tab>
<Tab value="Python">

```python title="me.py"
import os

import requests

response = requests.get(
    "https://api.matchai.co/v1/me",
    headers={"Authorization": f"Bearer {os.environ['MATCHAI_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()

me = response.json()
print(f"Connected as {me['display_name']}. Energy: {me['energy']['balance_state']}.")
```

</Tab>
</Tabs>

An abridged answer:

```json title="Response"
{
  "user_id": "3f1c9a2e-7b44-4d0e-9c1a-5e8f2b6d4a10",
  "professional_id": "8a2d4f6b-1c3e-4a5b-9d7f-0e2c4b6a8d1f",
  "username": "camilletremblay",
  "display_name": "Camille Tremblay",
  "job_title": "Residential real estate broker",
  "language": "fr-CA",
  "timezone": "America/Toronto",
  "site_id": "0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c",
  "page_url": "https://camilletremblay.matchai.pro",
  "energy": { "balance_state": "comfortable" }
}
```

`energy.balance_state` is `full`, `comfortable`, `low` or `empty`. Matchai Energy is never shown as a number. The names and ids in these examples are made up.

</Step>
<Step>

**List your listings.** `GET /v1/listings` returns the listings you can manage, most recently changed first. Lists come in pages: pass `limit` to size a page, and the `next_cursor` of one page as the `cursor` of the next.

<Tabs items={['curl', 'JavaScript', 'Python']}>
<Tab value="curl">

```bash title="Terminal"
curl -G https://api.matchai.co/v1/listings \
  -H "Authorization: Bearer $MATCHAI_API_KEY" \
  -d status=published \
  -d limit=5
```

</Tab>
<Tab value="JavaScript">

```js title="listings.mjs"
const url = new URL('https://api.matchai.co/v1/listings');
url.searchParams.set('status', 'published');
url.searchParams.set('limit', '5');

const response = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.MATCHAI_API_KEY}` },
});
if (!response.ok) throw new Error(`Matchai answered ${response.status}`);

const { data, next_cursor } = await response.json();
for (const listing of data) {
  console.log(`${listing.title} - ${listing.status} - ${listing.url}`);
}
console.log(next_cursor ? `More: cursor=${next_cursor}` : 'That is every listing.');
```

</Tab>
<Tab value="Python">

```python title="listings.py"
import os

import requests

response = requests.get(
    "https://api.matchai.co/v1/listings",
    headers={"Authorization": f"Bearer {os.environ['MATCHAI_API_KEY']}"},
    params={"status": "published", "limit": 5},
    timeout=30,
)
response.raise_for_status()

page = response.json()
for listing in page["data"]:
    print(f"{listing['title']} - {listing['status']} - {listing['url']}")
print(f"More: cursor={page['next_cursor']}" if page["next_cursor"] else "That is every listing.")
```

</Tab>
</Tabs>

An abridged answer, with one listing:

```json title="Response"
{
  "data": [
    {
      "id": "b7e4c2a1-9f3d-4e6b-8a5c-1d2f3e4a5b6c",
      "slug": "condo-rue-fabre-plateau-mont-royal",
      "url": "https://camilletremblay.matchai.co/listings/condo-rue-fabre-plateau-mont-royal",
      "status": "published",
      "availability": "available",
      "title": "Bright corner condo steps from Parc La Fontaine",
      "price": { "amount": 649000, "currency": "CAD" },
      "updated_at": "2026-10-18T09:41:52Z"
    }
  ],
  "next_cursor": null
}
```

To keep a copy of your listings in sync, pass `updated_since` with the time of your last sync. See [pagination](https://matchai.co/docs/developers/api-reference#pagination) for the full loop.

</Step>
<Step>

**Add a contact.** `POST /v1/contacts` adds a lead, a client or a professional. Send an `Idempotency-Key` with every `POST`: if your request times out, you can repeat it with the same key and Matchai will not add the contact twice.

<Tabs items={['curl', 'JavaScript', 'Python']}>
<Tab value="curl">

```bash title="Terminal"
curl https://api.matchai.co/v1/contacts \
  -H "Authorization: Bearer $MATCHAI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "lead",
    "first_name": "Julien",
    "last_name": "Bouchard",
    "email": "julien.bouchard@example.com"
  }'
```

</Tab>
<Tab value="JavaScript">

```js title="contact.mjs"
import { randomUUID } from 'node:crypto';

const response = await fetch('https://api.matchai.co/v1/contacts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MATCHAI_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': randomUUID(),
  },
  body: JSON.stringify({
    type: 'lead',
    first_name: 'Julien',
    last_name: 'Bouchard',
    email: 'julien.bouchard@example.com',
  }),
});

const body = await response.json();

if (response.status === 409 && body.error.code === 'contact_exists') {
  // The id of the contact that already has this email is in error.param.
  console.log(`Already a contact: ${body.error.param}`);
} else if (!response.ok) {
  throw new Error(`${body.error.code}: ${body.error.message}`);
} else {
  console.log(`Added ${body.display_name} as a ${body.type} (${body.id}).`);
}
```

</Tab>
<Tab value="Python">

```python title="contact.py"
import os
import uuid

import requests

response = requests.post(
    "https://api.matchai.co/v1/contacts",
    headers={
        "Authorization": f"Bearer {os.environ['MATCHAI_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "type": "lead",
        "first_name": "Julien",
        "last_name": "Bouchard",
        "email": "julien.bouchard@example.com",
    },
    timeout=30,
)
body = response.json()

if response.status_code == 409 and body["error"]["code"] == "contact_exists":
    # The id of the contact that already has this email is in error.param.
    print(f"Already a contact: {body['error']['param']}")
elif not response.ok:
    raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
else:
    print(f"Added {body['display_name']} as a {body['type']} ({body['id']}).")
```

</Tab>
</Tabs>

A `201` answer returns the new contact, abridged here:

```json title="Response"
{
  "id": "c5d8e1f2-3a4b-4c6d-9e8f-7a6b5c4d3e2f",
  "type": "lead",
  "first_name": "Julien",
  "last_name": "Bouchard",
  "display_name": "Julien Bouchard",
  "email": "julien.bouchard@example.com",
  "created_at": "2026-10-16T19:12:44Z"
}
```

When the email or phone number already belongs to one of your contacts, Matchai adds nothing and answers `409` with the code `contact_exists`. That is why the code above checks for it.

</Step>
</Steps>

## When a call asks first [#when-a-call-asks-first]

Some actions are **final actions**: sending a message, publishing a listing, deleting something. Your key's connection starts on **Ask me**, which means a final action does not run when you call it. It answers `202 Accepted` and waits for you to approve it in Matchai:

```json title="Response (202)"
{
  "status": "pending_approval",
  "approval_id": "1e2f3a4b-5c6d-4e7f-8a8b-9c0d1e2f3a4b",
  "approval_url": "https://matchai.co/approvals/1e2f3a4b-5c6d-4e7f-8a8b-9c0d1e2f3a4b",
  "expires_at": "2026-10-25T13:05:00Z",
  "summary": "Send a message to Julien Bouchard"
}
```

Treat `202` as "waiting for the person", never as done. [Final actions](https://matchai.co/docs/developers/authentication#final-actions) explains how to follow the outcome.

<Cards>
  <Card title="Authentication" href="https://matchai.co/docs/developers/authentication">
    Scopes, final actions, and when to use Connect with Matchai instead of a key.
  </Card>
  <Card title="API keys" href="https://matchai.co/docs/developers/authentication/api-keys">
    Create, scope, rotate and revoke keys.
  </Card>
  <Card title="Errors" href="https://matchai.co/docs/developers/errors">
    Every error code, and which calls are safe to retry.
  </Card>
  <Card title="API reference" href="https://matchai.co/docs/developers/api-reference">
    Every planned endpoint, starting with GET /v1/me.
  </Card>
</Cards>

## Sitemap

- [Every page of Matchai Docs](https://matchai.co/docs/sitemap.md)
- [llms.txt](https://matchai.co/docs/llms.txt)
