MCP API (Model Context Protocol)
The MCP endpoint exposes The Org API as Model Context Protocol (MCP) tools. Use it from MCP-compatible clients (Cursor, Claude, ChatGPT, and others) to query companies, people, open jobs, org charts, reporting lines, positions, contact details, people/lead lists, and usage over JSON-RPC 2.0.
For setup instructions (Claude Desktop, Cursor), see MCP Integration.
Endpoint
POST https://api.theorg.com/v1.1/mcp| Parameter | Value |
|---|---|
| Url | https://api.theorg.com/v1.1/mcp |
| Method | POST |
| Auth | OAuth (Authorization: Bearer) or X-Api-Key |
| Cost | Same credits as underlying APIs. Org-chart exploration is 10 credits per company per API key for 30 minutes |
| Server | 1.2 (serverInfo.version on initialize) |
Authentication
The MCP server supports OAuth in full, and it also accepts an API key. Credit costs match the underlying operations (list tools are free). An OAuth connection is issued its own API key, so credits, rate limits, and the 30-minute org-chart exploration pass are tracked on that connection separately from a key you paste yourself.
OAuth
Clients that implement MCP authorization connect with the server URL alone. A request without credentials returns 401 and a WWW-Authenticate header pointing at the protected-resource metadata, which is how the client starts the flow.
| Step | Endpoint |
|---|---|
| Protected resource | GET https://api.theorg.com/.well-known/oauth-protected-resource/v1.1/mcp |
| Authorization server | GET https://api.theorg.com/.well-known/oauth-authorization-server (also …/oauth-authorization-server/v1.1/mcp) |
| OpenID configuration | GET https://api.theorg.com/.well-known/openid-configuration |
| Dynamic client registration | POST https://api.theorg.com/oauth/register |
| Authorize | GET https://api.theorg.com/oauth/authorize |
| Token | POST https://api.theorg.com/oauth/token |
| Userinfo | GET https://api.theorg.com/oauth/userinfo |
The grant is authorization code with PKCE. code_challenge_method must be S256. Clients are public (token_endpoint_auth_method is none). Supported scopes are openid, email, and mcp. Client ID metadata documents are accepted. After you approve the connection on The Org, the token response is a Bearer access token with expires_in of 365 days. Send it on MCP calls as Authorization: Bearer.
Userinfo returns sub, email, and email_verified.
API key
Pass the same API key as the rest of the API in the X-Api-Key header, or as the api_key query parameter (apikey and x-api-key are also accepted). The examples below use the header.
Optional: send MCP-Protocol-Version: 2025-11-25 (or 2025-03-26) on requests.
Protocol
The transport is Streamable HTTP : each JSON-RPC message is a separate POST with a JSON body.
- Initialize – Send an
initializerequest; the server returns capabilities and server info. - Initialized – Send a
notifications/initializednotification (server responds 202 Accepted). - Tools – Use
tools/listto discover tools andtools/callto invoke them.
HEAD, and GET without Accept: text/event-stream, return 200 so clients can probe the URL. SSE streaming is not supported. Send JSON-RPC messages with POST.
Tools
| Tool | Description | Credits | Rate limit |
|---|---|---|---|
search_companies | Look up companies by name, domain, email, or LinkedIn URL/slug | Free | 30 / 60s |
get_company | Enriched company profile by company ID, domain, or LinkedIn URL/slug | Free | 60 / 60s |
get_org_chart | Iframe embed URL for a company org chart (by domain or LinkedIn URL/slug) | Free | 60 / 60s |
get_company_executive_positions | Company context plus L1, L2, and advisers. Each entry can be a person, a job, or a group | 10 / company / key / 30 min | Global 15/s |
get_company_position_reports | Direct reports of one chart position (p-… person or job, g-… group) | Same exploration pass | Global 15/s |
search_company_positions | Search one company’s chart by name or title. Results can be people, jobs, or groups | Same exploration pass | Global 15/s |
find_person | Look up a person by LinkedIn URL/slug, or full name + company name/domain | Free | 30 / 60s |
find_jobs | Search open jobs by URL/slug, title, company, or filters (max 25) | Free | 30 / 60s |
get_manager | Manager of a person (position ID/email/LinkedIn URL/slug) | 1 per find | Global 15/s |
get_reports | Direct reports of a person (position ID/email/LinkedIn URL/slug) | 1 when reports are found | Global 15/s |
find_positions | Search positions/people with filters (max 25 results per call) | 1 per row | Global 15/s |
resolve_contacts | Actively resolve work emails for up to 25 people from prior lookups | 1 per person newly resolved | Global 15/s |
get_lists | List people/lead lists for the account (owned and shared) | Free | 30 / 60s |
add_to_list | Add position IDs from lookups to an existing list (max 25) | Free | Global 15/s |
create_list | Create a people/lead list and optionally add position IDs (max 25) | Free | Global 15/s |
get_usage | Current account usage/credits | Free | 30 / 60s |
List tools return a url for each list in the form https://theorg.com/people/lists/{slug} (for example https://theorg.com/people/lists/empty-list). The same lists can be read over HTTP via the Lists API (GET /v1.1/lists).
Free-tool windows are per API key and shared with the matching REST route. Details: Rate Limiting.
Every tool declares a JSON Schema outputSchema in tools/list, and successful tools/call results carry the typed payload in structuredContent alongside the human-readable content text block.
Recommended routing
| Intent | Tool |
|---|---|
| Resolve a company from a name or LinkedIn | search_companies |
| Tell me about this company | get_company (description, size, location, industries, funding) |
| Show a company’s org chart | get_org_chart, then render the returned embedUrl in an iframe |
| Who runs this company, then walk the chart | search_companies, then get_company_executive_positions, then get_company_position_reports or search_company_positions |
| Look up a named person / LinkedIn profile | find_person |
| Find open jobs | find_jobs |
| Who manages this person? | get_manager |
| Who reports to this person? | get_reports |
| Who is the CEO / people in a department? | find_positions with companyDomains + jobTitles / departments |
| Get someone’s work email | find_person or find_positions, then resolve_contacts with the returned position IDs |
| Build a lead list from a search | find_positions then create_list with name + positionIds |
| Add people to an existing list | get_lists (if needed) then add_to_list with listId or slug + positionIds |
| What lists do I have? | get_lists |
| Remaining credits | get_usage |
Filter fields for find_positions match the Position API. Company/org-chart tools match the Company API. MCP find_positions and find_jobs limit results to 25 per call (stricter than the Position API’s page size of up to 1000).
Company tools
search_companies
Look up companies and return ids, names, domains, org-chart URLs, and signed embed URLs. Provide any one of the following:
| Argument | Type | Description |
|---|---|---|
q | string | Company name search query |
domain | string | Company website or email domain (e.g. theorg.com) |
email | string | Work email used to resolve the company |
linkedInUrl | string | LinkedIn company URL or bare slug (e.g. https://www.linkedin.com/company/stripe or stripe) |
limit | integer | Optional. Max results (1–50, default 10) |
get_company
Returns an enriched company profile. Provide exactly one identifier.
| Argument | Type | Description |
|---|---|---|
companyId | string | Company UUID from search_companies or find_positions |
domain | string | Company website or email domain (e.g. theorg.com) |
linkedInUrl | string | LinkedIn company URL or bare slug (e.g. stripe) |
The response company object includes id, name, slug, description, logoUrl, domains, websiteUrl, linkedInUrl, twitterUrl, industry, industries, legalStatus, stage, foundedYear, employeeRange, location, totalFundingUsd, latestFundingStage, crunchbaseUrl, positionCount, followerCount, verified, orgChartUrl, and embedUrl.
industries, legalStatus, employeeRange, and latestFundingStage use the same vocabulary as the find_positions filters, so a company profile can be fed straight back into a people search.
get_org_chart
Returns an iframe embed URL for a company’s org chart rather than the chart data itself, which keeps large charts out of the model’s context.
| Argument | Type | Description |
|---|---|---|
domain | string | Company website or email domain (e.g. theorg.com) |
linkedInUrl | string | LinkedIn company URL or bare slug (e.g. stripe) |
The response contains name, embedUrl, and orgChartUrl. Render embedUrl in an iframe; it is signed to your API key. To walk the chart as JSON, use the exploration tools below. The HTTP Company API org chart endpoint is a separate charge (10 credits per request).
Org-chart exploration
get_company_executive_positions, get_company_position_reports, and search_company_positions read one company’s chart. If the question is about that company’s org chart structure, these tools need companyId, a UUID. Identify the company and that id with search_companies (or get_company) first. A name or slug is rejected.
The first call that finds the company, on a given API key and company, costs 10 credits and opens a 30-minute exploration pass. An OAuth connection counts as its own API key. An unknown company id is not charged. Further calls for that pair inside the window are free, including paging get_company_position_reports and search_company_positions. A different company, or the same company on another key or connection, starts a new pass. If fewer than 10 credits remain, the tool result is an error and no pass is stored. This window is separate from the 24-hour replay on metered REST requests.
These tools do not replace get_manager / get_reports, which look a person up by position id, email, or LinkedIn and cost 1 credit per find.
get_company_executive_positions
Company profile fields plus the top of the chart: l1 (roots), l2 (their direct reports), advisers, and section counts (placed, unplaced, board, total). Each entry can be a person, a job, or a group.
| Argument | Type | Description |
|---|---|---|
companyId | string | Company UUID (required) |
Each entry includes nodeId, title, name, linkedInUrl, theOrgUrl, nodeType (position for a person, job, or coManager for a group), section (orgChart, unplaced, or board), parentNodeId, reportCount, and seniorityScore. name is null for jobs and groups.
get_company_position_reports
Direct reports of one person, job, or group. This is not get_reports, which looks a person up by position id, email, or LinkedIn.
| Argument | Type | Description |
|---|---|---|
companyId | string | Company UUID (required) |
nodeId | string | Id of the person, job, or group from get_company_executive_positions or search_company_positions (p-… or g-…, required) |
limit | integer | Page size (default 50, max 50) |
offset | integer | Page offset (default 0) |
The response contains node, reports, totalReports, limit, offset, and hasMore.
search_company_positions
Search that company’s chart by name or title. Matches people, jobs, and groups.
| Argument | Type | Description |
|---|---|---|
companyId | string | Company UUID (required) |
query | string | Name or title text |
section | string | Optional filter: orgChart, unplaced, or board |
limit | integer | Page size (default 50, max 50) |
offset | integer | Page offset (default 0) |
The response contains companyId, results, and totalCount.
Person and job lookup tools
find_person
Look up a specific person in The Org’s people index. Prefer this over find_positions when you already have a LinkedIn profile or a name+company pair.
Provide either linkedInUrl, or fullName with companyName or companyDomain:
| Argument | Type | Description |
|---|---|---|
linkedInUrl | string | LinkedIn profile URL or bare slug (e.g. https://www.linkedin.com/in/jane-doe or jane-doe) |
fullName | string | Person full name (use with companyName or companyDomain) |
companyName | string | Employer company name (use with fullName) |
companyDomain | string | Employer website/email domain (use with fullName) |
limit | integer | Optional. Max results for name lookups (1–10, default 5) |
The response contains people entries with positionId, fullName, title, slug, linkedInUrl, companyId, companyName, companySlug, profileUrl, and orgChartUrl. Use returned positionId values with get_manager, get_reports, resolve_contacts, and list tools.
find_jobs
Search open job vacancies. Resolve a specific posting via jobUrl or companySlug + jobSlug, or search with title/keywords and optional company/filters.
| Argument | Type | Description |
|---|---|---|
jobUrl | string | Full The Org job URL (https://theorg.com/org/{companySlug}/jobs/{jobSlug}) |
companySlug | string | Company slug (use with jobSlug) |
jobSlug | string | Job post slug (use with companySlug) |
jobTitle | string | Job title phrase (e.g. Product Manager) |
companyName | string | Hiring company name |
companyDomain | string | Hiring company website/email domain |
companyId | string | Company UUID from search_companies |
keywords | string[] | Additional job-title keywords |
jobTypes | string[] | Employment types (snakecase, e.g. full_time) |
jobFunctions | string[] | Functional areas (snakecase, e.g. engineering) |
countries | string[] | Job-location ISO country codes (e.g. US, GB) |
remote | boolean | When true, only remote jobs; when false, only non-remote |
limit | integer | Optional. Max results for searches (1–25, default 5) |
The response contains jobs and totalResults. Each job includes jobId, title, slug, teaser, remote, jobType, jobFunction, country, company fields, jobUrl, and optional associatedPositionId when the role is placed on the org chart.
Reporting-line tools
get_manager walks one step up the reporting line and get_reports walks one step down. Both accept the same identifiers — provide any one of them.
| Argument | Type | Description |
|---|---|---|
positionId | integer | Position ID from find_positions or find_person |
email | string | Work email of the person |
linkedInUrl | string | LinkedIn profile URL or bare slug |
get_manager returns data.position and data.manager (null when the person is at the top of the chart). get_reports returns data.position, data.reports, and data.totalReports. At most 50 reports are returned per call; totalReports always carries the true count.
Both cost 1 credit per successful lookup — get_manager when a manager is found, get_reports when at least one report is found.
find_positions arguments
| Argument | Type | Description |
|---|---|---|
limit | integer | Required. Max results (1–25) |
offset | integer | Required. Pagination offset (0–10000) |
filters | object | Same fields as the Position API (see below) |
find_positions filters
filters accepts the same fields as the Position API, including:
| Field | Type | Description |
|---|---|---|
companyDomains | string[] | Company domains (e.g. apple.com) |
jobTitles | string[] | Role title phrases (include variants: CEO, Chief Executive Officer) |
departments | string[] | Department snakecase values (e.g. marketing, engineering) |
personFullNames | string[] | Named individuals |
managerFullNames / managerJobTitles | string[] | People reporting to a manager |
industries | string[] | Industry labels |
locations | { country, state? }[] | Company location filters |
employeeRanges | string[] | Company size buckets (e.g. 1-10, >10000) |
orgChartLevels | string[] | LevelOne … LevelFourPlus, Unplaced |
companyLegalStatuses | string[] | Legal/entity types |
latestFundingStages | string[] | Funding stages |
previousCompanyDomains | string[] | Previous employer domains |
hiredWithinDays | integer | Recent hires |
verifiedWorkEmail | boolean | Only rows with a verified email already stored. Use sparingly — it hides people whose email resolve_contacts could still find |
personIds | integer[] | Internal position IDs |
resolve_contacts
Actively resolves work emails for people you have already found via find_positions or find_person.
| Argument | Type | Description |
|---|---|---|
positionIds | integer[] | Position IDs from find_positions or find_person (required, max 25 per call) |
The workEmail field on a find_positions row only reflects whether an email is already stored — it is not a resolution attempt. A null there does not mean no email can be found. Call resolve_contacts before concluding that contact details are unavailable.
The response contains data.creditsUsed and data.results, with one entry per requested person holding positionId, emails (each with email, emailType, and verification), and phoneNumbers. A person with no resolvable email comes back with empty arrays rather than being omitted.
Credits: 1 credit per person newly resolved to an email. People this account has resolved before are free, and people for whom no email is found cost nothing. If the batch is larger than your remaining balance, only as many people as you can afford are attempted, so a call never overdraws. Phone numbers are read from existing data and are never charged for.
People / lead list tools
Use find_positions or find_person first, then pass returned position IDs into create_list or add_to_list.
get_lists
| Argument | Type | Description |
|---|---|---|
limit | integer | Optional. Max lists to return (1–100, default 30) |
offset | integer | Optional. Pagination offset (default 0) |
Response includes lists (each with id, name, slug, url, optional positionCount) and total.
add_to_list
| Argument | Type | Description |
|---|---|---|
listId | string | List UUID from get_lists (provide listId or slug) |
slug | string | List slug (e.g. empty-list) |
positionIds | integer[] | Position IDs from find_positions (required, max 25 per call) |
create_list
| Argument | Type | Description |
|---|---|---|
name | string | Display name for the new list (required) |
positionIds | integer[] | Optional position IDs to add immediately (max 25) |
Response includes the created/updated list (id, name, slug, url) and addedCount.
Example: Initialize
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": { "name": "example", "version": "1.0" }
}
}'Example: List tools
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'Example: Call a tool (get_usage)
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": { "name": "get_usage", "arguments": {} }
}'Example: Call get_org_chart
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "get_org_chart",
"arguments": { "domain": "theorg.com" }
}
}'Example: Call get_company
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "get_company",
"arguments": { "domain": "theorg.com" }
}
}'Example: Call find_person
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 12,
"method": "tools/call",
"params": {
"name": "find_person",
"arguments": {
"fullName": "Patrick Collison",
"companyName": "Stripe"
}
}
}'Example: Call find_jobs
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 13,
"method": "tools/call",
"params": {
"name": "find_jobs",
"arguments": {
"jobTitle": "Product Manager",
"companyName": "Stripe",
"limit": 5
}
}
}'Example: Call find_positions
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "find_positions",
"arguments": {
"limit": 5,
"offset": 0,
"filters": {
"companyDomains": ["theorg.com"],
"jobTitles": ["CTO", "Chief Technology Officer"]
}
}
}
}'Example: Call get_reports
Use a position id returned by find_positions:
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "get_reports",
"arguments": { "positionId": 12345 }
}
}'Example: Call resolve_contacts
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "resolve_contacts",
"arguments": { "positionIds": [12345, 12346] }
}
}'Example: Call get_lists
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 9,
"method": "tools/call",
"params": {
"name": "get_lists",
"arguments": { "limit": 30, "offset": 0 }
}
}'Example: Call create_list
After find_positions, pass returned position IDs:
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "create_list",
"arguments": {
"name": "Fintech CTOs",
"positionIds": [12345, 12346]
}
}
}'Example: Call add_to_list
curl --location 'https://api.theorg.com/v1.1/mcp' \
--header 'X-Api-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "add_to_list",
"arguments": {
"slug": "empty-list",
"positionIds": [12345, 12346]
}
}
}'Error responses use standard JSON-RPC 2.0 errors or tool results with isError: true and a message in the content.