Search profiles with SQL filters

POST https://api.pearch.ai/v2/search/sql

Search professional profiles using sql_filter, an optional ranking query, or both. Results use the same search_results[].profile structure as /v2/search.

📘

Meaning versus constraints

Put what you want to rank by in query, and conditions every result must satisfy in sql_filter. Omit query for a filters-only search.

Quick start

import os
import requests

response = requests.post(
    "https://api.pearch.ai/v2/search/sql",
    headers={"Authorization": f"Bearer {os.environ['PEARCH_API_KEY']}"},
    json={
        "query": "Python backend engineers building distributed systems",
        "sql_filter": "location_country_code = 'US' AND total_experience_years >= 5",
        "limit": 10,
    },
    timeout=60,
)
response.raise_for_status()
for result in response.json()["search_results"]:
    print(result["docid"], result["profile"])
curl --request POST \
  --url https://api.pearch.ai/v2/search/sql \
  --header "Authorization: Bearer $PEARCH_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"query\":\"Python backend engineers\",\"sql_filter\":\"location_country_code = 'US'\",\"limit\":10}"

Use your usual Pearch API key in the Authorization: Bearer <API_KEY> header.

Find profiles located in the United States with at least five years of experience, using only filters:

{
  "sql_filter": "location_country_code = 'US' AND total_experience_years >= 5",
  "limit": 10
}

Request parameters

ParameterTypeDefaultDescription
querystring or nullnullOptional text used to rank profiles. Omitted, null, or blank values select filters-only search.
sql_filterstring""SQL condition that results must satisfy, up to 16,384 characters. Omit or leave empty to apply no SQL filter.
limitinteger10Maximum profiles to return, from 1 through 1000. Fewer profiles can be returned.

Provide a nonempty query or sql_filter. Requests with neither return HTTP 400.

Write SQL filters

See the SQL language and complete schema for every field, type, supported filtering operation, repeated-record scope, and examples for company experience, education, skills, languages, geography, activity, and funding.

Use LOCATION('Athens, Greece', 'GR') for a named place, or LOCATION('New York, United States', 'US', 50, 'km') for a distance radius. Supported time zones can be filtered with location_timezone. See geographic filters for the full syntax.

Combine conditions with AND, OR, and parentheses in one string:

{
  "query": "Infrastructure engineer",
  "sql_filter": "(location_country_code = 'US' OR location_country_code = 'CA') AND total_experience_years BETWEEN 5 AND 10",
  "limit": 20
}

A leading WHERE is accepted. Send a SQL condition rather than a complete top-level SELECT, ORDER BY, or LIMIT. When supplied, query ranks results; limit controls the maximum result count. SELECT is valid inside supported EXISTS and aggregate subqueries.

SQL follows the grouped response structure. For example, require a current engineering role at Stripe:

EXISTS (
  SELECT 1
  FROM UNNEST(experiences) AS c
  CROSS JOIN UNNEST(c.company_roles) AS r
  WHERE c.company_info.domain = 'stripe.com'
    AND SEARCH(r.title, 'engineer')
    AND r.end_date IS NULL
)

Response

HTTP 200 returns ranked profiles in the search response. Profiles that could not be retrieved are skipped. This example is shortened; profile fields depend on available data.

{
  "uuid": "a4862482-9067-4acd-b229-74a44de26a99",
  "query": "Python backend engineers",
  "status": "Done",
  "search_results": [
    {
      "docid": "example-engineer",
      "profile": {
        "linkedin_slug": "example-engineer",
        "first_name": "Alex",
        "title": "Backend Engineer",
        "skills": ["Distributed systems"],
        "canonical_skills": ["Python"],
        "experiences": [
          {
            "company_info": {"name": "Example", "domain": "example.com"},
            "company_roles": [{"title": "Backend Engineer", "start_date": "2022-01-01"}]
          }
        ]
      }
    }
  ],
  "credits_used": 1,
  "credits_remaining": 99,
  "credits_breakdown": {
    "version": 1,
    "total": 1,
    "items": [{"component": "search", "data_point_type": "profile", "quantity": 1, "unit_cost": 0.2, "credits": 1}]
  }
}

search_results is ordered by relevance when query is supplied. Filters-only result order is unspecified, and the response query is an empty string. Additional response fields include created_at, duration, user, and parameters.

Profiles include grouped company_roles, free-text skills, and normalized canonical_skills. SQL exposes these skill names, grouped company and role paths, summary, and inferred_languages; filter-only extensions and field operations are documented in the schema reference.

Pricing and limits

The initial price is 0.2 credits per returned profile. Round up the total once per request:

credits_used = ceil(number_of_profiles_returned × 0.2)

Balance checks require enough credits for the requested limit; the final charge covers only returned profiles.

The endpoint shares the actual-search rate-limit bucket with /v2/search; the default is 60 requests per user per 60 seconds, subject to organization configuration.

Errors

HTTP statusMeaning
400Invalid parameters, unsupported parameters, missing both query and filters, invalid SQL, or unsupported SQL fields or constructs.
402Insufficient credits for the requested result limit.
403Missing or invalid API credentials, or access denied by the existing authentication policy.
429Shared search rate limit exceeded.
500An internal failure.
502The search service is unavailable or returned an error.
{"http": 400, "status": "Invalid sql_filter"}