API reference
The Moat API uses plain HTTPS requests with form-encoded bodies and returns JSON. Profiles, companies and services each have their own endpoints.
Base URL#
https://moat.page
Paths below are relative to this origin. Endpoints are available with or without a .php suffix.
Authentication#
| Method | Where it's used | What it unlocks |
|---|---|---|
X-API-Key header | Your server only | All partner endpoints, full profiles, email and phone |
X-Private-Key header | Your server, for one person | That person's full profile, including email and phone |
public_key parameter | Browsers and servers | Public and full profile levels, never contact details |
| None | Anywhere | Card and public levels of public profiles, public directories |
Responses and errors#
Every response is JSON with a boolean success. Failures add an error string and use a matching HTTP status.
{ "success": false, "error": "private_key, public_key, or email required" }| Status | Meaning |
|---|---|
| 200 | Success. Check fields such as created or updated for what changed. |
| 400 | A required field is missing or invalid. |
| 401 | Missing or wrong key, or a level that needs more authentication. |
| 403 | Not allowed, for example account_suspended or editing a company you didn't create. |
| 404 | No profile, company or service matched. |
| 429 | Rate limit reached on a public endpoint. Retry after a short wait. |
| 500 | Something failed on our side. Safe to retry get-or-create and update calls. |
Identifying a person#
Endpoints that act on one profile accept private_key, public_key or email, tried in that order. Prefer the private key on servers. It's the most specific and it doesn't change if the person changes their email.
Profiles#
Check for a profile
Returns whether a person has a Moat page, and their keys if they do. Send one of email, public_key or private_key.
{
"success": true,
"has_profile": true,
"profile_key": "7qzwrjebfpaw",
"profile_url": "https://moat.page/profile/d/amara-okafor-7qzwrjebfpaw",
"public_key": "c6424007df52eee0ee82483f7c464dc3",
"private_key": "prv_..."
}Get or create a profile
Returns the existing profile matched by private key, public key or email, or creates one. Existing data is never overwritten. created tells you which happened.
| Field | Required | Description |
|---|---|---|
email or user_key | Yes | Who the profile belongs to |
first_name, last_name, public_chat_name | No | Name and display name |
profile_type | No | professional, employer or provider |
current_title, current_company, headline, about | No | Starter content for a new page |
country, state_region, city, timezone | No | Location, with ISO 3166 country codes |
skills, experience, education | No | JSON arrays, for example [{"value":"Figma","proficiency":"expert"}] |
profile_flags | No | Comma-separated status signal keys |
site_url, site_name | With any site field | Adds your app to the person's connected apps |
callback_site_param | Recommended | Stable key for your app, used to match and remove your entry |
user_update_path | For webhooks | HTTPS URL that receives events for this person |
site_logo_url, site_edit_url | No | Branding and an edit link shown to the person |
Update a profile
Overwrites the fields you send and leaves the rest alone. Accepts the same fields as create, plus contact_phone. Returns 404 if no profile matches, so fall back to create. Returns "updated": false when nothing changed.
Connect or disconnect your app
Adds or updates your app in the person's connected apps without touching anything else. Send remove=1 with your callback_site_param to disconnect. Removing your own entry doesn't trigger a webhook back to you.
Read a profile
| Parameter | Description |
|---|---|
public_key | The person's public key |
level | strip (card), public or full |
viewer_key | Optional public key of the viewer, adds is_following |
Rate-limited to 60 requests per minute per IP. Results sit under output.user. The widget docs list the fields in each level.
Directory
Paginated lists of complete profiles. Filter by site_name to list only people connected from your app.
| Parameter | Description |
|---|---|
search | Name, headline, title, company or city |
site_name | Only profiles connected from this app |
flag_slug | A curated audience, such as open-to-work or need-mentor. Also available as /profile/api/directory/flag/{slug} |
skill_slug | People with a skill, such as machine-learning. Also /profile/api/directory/skill/{slug} |
profile_type, industry, country, status, skills | Field filters. skills must all match |
page, per_page | 1-based page, up to 50 per page (default 20) |
Companies#
Create needs user_key and name and returns company_key and company_url. Update is partial and needs item_key plus the creator's user_key. Anyone else gets 403. Directory filters: search, industry, company_type, company_size, page, per_page. get_company accepts level=strip|public|full.
Services#
Same shape as companies. Directory filters: search, service_type, pricing_model, delivery, page, per_page.
Follows and connections#
Follow, connection and messaging endpoints are available to approved partners and documented in the partner handbook we share when your access is set up.