Skip to main content
POST
/
person
/
search
Search persons using SQL query
curl --request POST \
  --url https://api.datalegion.ai/person/search \
  --header 'API-Key: <api-key>' \
  --header 'Content-Type: application/json' \
  --data @- <<EOF
{
  "query": "SELECT * FROM people WHERE city = 'San Francisco' ORDER BY last_seen DESC",
  "limit": 10,
  "titlecase": false
}
EOF
{
  "matches": [
    {
      "person": {
        "legion_id": "fdd85569-f0f0-53a9-bc60-089507193c28",
        "full_name": "jane marie doe",
        "first_name": "jane",
        "last_name": "doe",
        "city": "san francisco",
        "state": "california",
        "state_code": "US-CA",
        "country": "united states",
        "country_code": "US",
        "job_title": "senior product manager",
        "company_name": "tech company",
        "company_domain": "techcompany.com",
        "company_industry": "technology, information and internet",
        "company_size": "1001-5000",
        "seniority_level": "senior",
        "job_function": "product",
        "work_email": "jane.doe@techcompany.com",
        "linkedin_url": "https://www.linkedin.com/in/janedoe",
        "linkedin_id": "123456789",
        "years_of_experience": 12,
        "last_seen": "2026-01-20"
      }
    }
  ],
  "total": 8492
}

Query Format

Write a SELECT * FROM people WHERE ... query. Do not include LIMIT or OFFSET clauses in the SQL — use the limit and offset parameters instead.
{
  "query": "SELECT * FROM people WHERE company_name ILIKE '%google%' AND state = 'california'",
  "limit": 5
}

Response Shape

{
  "matches": [ { "person": { "..." : "..." } } ],
  "total": 8492
}
  • matches — capped at the request’s limit (max 100).
  • total — the full row count matching your WHERE clause across the database, not just the returned page. Useful for TAM-style sizing: query for “VPs of Engineering at fintech in NY” with limit: 1 and total tells you how many candidates exist. The companion Total-Count-Status response header is exact when the count succeeded; on extremely broad WHERE clauses the count subquery may time out, in which case the header is page and total is just the size of the returned page. Search responses do not include match_metadata (that field is only populated by /person/enrich, where there’s a single input identifier to match against).

Pagination

To page through results beyond the first 100, set offset to the number of rows to skip. Maximum offset is 10,000.
{
  "query": "SELECT * FROM people WHERE company_name ILIKE '%google%' AND state = 'california'",
  "limit": 100,
  "offset": 100
}
Ordering is deterministic within a build — the same request returns the same rows in the same order, so paged requests don’t overlap or skip rows. Builds run on a periodic cadence; ordering may change across builds, so paginate within a single client session, not across days.

Available Columns

Text Columns

Use ILIKE for partial matching or = for exact matching.
ColumnDescription
full_nameFull name
first_nameFirst name
last_nameLast name
job_titleCurrent job title
company_nameCurrent company name
company_domainCurrent company domain
company_industryCurrent company industry
cityCurrent city
stateCurrent state (full name, e.g., california)
state_codeState code (ISO 3166-2, e.g., US-CA)
countryCurrent country (full name, e.g., united states)
country_codeCountry code (ISO 3166-1 alpha-2, e.g., US)

Numeric Columns

ColumnDescription
ageCurrent age
birth_yearBirth year
years_of_experienceTotal years of professional experience
avg_tenure_monthsAverage tenure across jobs (months)
linkedin_followersLinkedIn follower count
linkedin_connectionsLinkedIn connection count

Enum Columns

Use = or IN for exact matching.
ColumnValues
sexmale, female
seniority_levelc_level, owner, partner, vp, director, manager, senior, junior, training, intern
job_functionengineering, information_technology, product, design, sales, marketing, operations, finance, hr, legal, customer_success, data, executive, social_services, healthcare, education, trades, transportation, service, other
expense_categorygeneral_and_administrative, research_and_development, sales_and_marketing, cost_of_services, not_applicable
company_size1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+
highest_degree_leveldoctorate, masters, bachelors, associates, high_school

Boolean Columns

ColumnValues
is_decision_makertrue, false

Cross-Table Columns

These columns search across related tables and are automatically rewritten to subqueries:
ColumnDescription
skillsSkills from all positions
languagesSpoken languages
headlineLinkedIn headline text
summaryLinkedIn summary / bio text
titleAll job titles from work history (not just current)
organization_nameAll employer names from work history
descriptionJob description text from work history

Example Queries

-- Engineers at Google in California
SELECT * FROM people WHERE company_name ILIKE '%google%' AND job_title ILIKE '%engineer%' AND state = 'california'

-- Senior leadership at large companies
SELECT * FROM people WHERE seniority_level IN ('vp', 'c_level', 'director') AND company_size = '10001+'

-- People with Python skills in New York
SELECT * FROM people WHERE skills ILIKE '%python%' AND state = 'new york'

-- Decision makers in marketing
SELECT * FROM people WHERE is_decision_maker = true AND job_function = 'marketing'

Authorizations

API-Key
string
header
required

Body

application/json

Request model for SQL query-based person search.

query
string
required

SQL query to execute (must not contain LIMIT clause). Must be a SELECT query only.

Required string length: 1 - 10000
limit
integer
required

Maximum number of results to return (replaces LIMIT in query)

Required range: 1 <= x <= 100
offset
integer
default:0

Number of results to skip for pagination (0-10000). Stable within a build; ordering may change across builds.

Required range: 0 <= x <= 10000
titlecase
boolean
default:false

If true, format text fields in title case (names, job titles, company names, locations, skills, headlines). Raw fields, IDs, URLs, codes, and confidence fields are excluded.

include_fields
string | null

Comma-separated list of fields to include in response. If omitted, all fields are returned.

exclude_fields
string | null

Comma-separated list of fields to exclude from response. Applied after include_fields filter.

pretty_print
boolean
default:false

If true, pretty-print JSON response with indentation.

Response

Success - query executed and results returned

Multiple person matches response.

matches
PersonMatchResponse · object[]
required

List of matches sorted by confidence (descending). Capped at the request's limit.

total
integer
required

For /person/search and /person/discover, the total number of rows matching the query's WHERE clause across the database (not just this page). For /person/enrich, the number of matches found for the input identifier. On search/discover, if the count subquery fails or times out, this falls back to the size of the returned page; the response header Total-Count-Status distinguishes the two cases.