Play Video
clay logo

Oppora in Clay

Use Clay's built-in HTTP API enrichment column to call Oppora directly from any table. Save your API key once as a workspace-level HTTP API account, then drop the recipes below into any column.

Before you start

  • 1An Oppora API key (Integrations → Oppora API in the app). You'll see one secret starting with opp_live_… — copy it once; it's never shown again.
  • 2A paid Oppora plan (Pro or Max). API keys can't be generated on Free.
  • 3A Clay workspace with at least one table containing the input columns the recipe expects (e.g. first_name, last_name, company_domain).

Save your Oppora credentials once

Clay's HTTP API (Headers) account stores your Bearer token encrypted at the workspace level. Set it up once and every enrichment column reuses it — no pasting the key into every cell.

  1. In Clay, click your workspace name (top-left) → SettingsConnections.
  2. Click Add connection → pick HTTP API (Headers).
  3. Name it Oppora. In the headers section, add a single row:
Header
Authorization: Bearer YOUR_KEY

Save. Now every recipe below picks Oppora from the "Account" dropdown instead of asking for headers each time.

Recipes

Each recipe is one Oppora endpoint. In Clay: Add enrichment HTTP APIConfigure tab → paste the URL, method, and body below. Reference your table columns inside the body with {{ column_name }}.

POST /email/search

Find email by name + domain

Resolves a verified work email when you have a person's name and their company's website domain.

Request body
{
  "first_name": "Alice",
  "last_name": "Chen",
  "domain": "stripe.com"
}
Response
{
  "email": "[email protected]",
  "status": "valid",
  "source": "oppora",
  "credit_charged": 1,
  "credits_remaining": 4823
}
  • `domain` is required — free-text company names are not accepted (they cause wrong-company matches).
  • Credit is charged only when an email is returned with status `valid` or `risky`. Misses are free.
Wire it in Clay

Set Method = POST, URL = https://api.oppora.ai/api/v1/public/email/search, Body type = JSON. Paste the body and swap the values for column refs:

Clay body (with column refs)
{
  "first_name": "{{ first_name }}",
  "last_name":  "{{ last_name }}",
  "domain":     "{{ company_domain }}"
}

On the response, map $.email and $.status into new columns. Free on misses (no credit charged when status is unknown).

POST /email/verify

Verify an email

Check deliverability before sending. Use `mode: "advanced"` for catch-all domains where `standard` returns `risky` or `unknown`.

Request body
{
  "email": "[email protected]",
  "mode": "standard"
}
Response
{
  "email": "[email protected]",
  "mode": "standard",
  "status": "valid",
  "credit_charged": 1,
  "credits_remaining": 4822
}
  • `mode` is `standard` (fast) or `advanced` (deep SMTP, catch-all aware). Both charge on result.
Wire it in Clay

Useful as a second column right after email_search for catch-all domains. Use "mode": "advanced" in a third column for risky / unknown rows.

Clay body
{
  "email": "{{ email }}",
  "mode":  "standard"
}
POST /phone/search

Lookup phone from LinkedIn URL

Best with a LinkedIn URL. Falls back to (name + company) or (name + domain). DB-cache hits are free; external provider hits charge 1 phone credit on success.

Request body
{
  "linkedin_url": "https://www.linkedin.com/in/alice-chen-vp"
}
Response
{
  "phone": "+14155551234",
  "status": "valid",
  "source": "oppora_db",
  "credit_charged": 0,
  "credits_remaining": 1200
}
  • `source: "oppora_db"` = pulled from cache (free). External provider sources (wiza, dropcontact, etc.) charge 1 credit per resolved phone.
Wire it in Clay

LinkedIn URL is the strongest signal. For rows that don't have one, fall back to name + company/domain:

Clay body — LinkedIn URL preferred
{
  "linkedin_url": "{{ linkedin_url }}"
}
Clay body — name + domain fallback
{
  "first_name": "{{ first_name }}",
  "last_name":  "{{ last_name }}",
  "domain":     "{{ company_domain }}"
}
POST /discover/companies

Discover companies matching ICP filters

Search the global company DB by industry, size, location, revenue, funding, growth. Returns up to 100 rows per page with cursor pagination. Every row carries an Oppora `id` so you can chain into add-to-list flows.

Request body
{
  "industry": ["Software Development"],
  "hq_country": "USA",
  "size": ["51-200", "201-500"],
  "revenue_range_min": 5000000,
  "limit": 25
}
Response
{
  "data": [
    {
      "id": 8421,
      "name": "Stripe",
      "domain": "stripe.com",
      "industry": "Financial Services",
      "size": "5001-10000",
      "employee_count": 8200,
      "year_founded": 2010,
      "total_funding_usd": 8700000000,
      "country": "USA"
    }
  ],
  "count": 25,
  "total": 1842,
  "next_cursor": "eyJjdXJzb3IiOiIuLi4ifQ",
  "has_more": true,
  "credit_charged": 1,
  "credits_remaining": 4821
}
  • Resolve exact filter values via `GET /filters/industries` and `GET /filters/countries` first — wrong strings match zero rows.
  • `hq_country` is a single ISO 3-alpha code ("USA", "GBR", "IND").
Wire it in Clay

Use Clay's "Use as source" option (instead of enrichment) to populate a new table from Oppora's company database. Map $.data[*] as the row source — each item becomes a row.

For pagination: store $.next_cursor and re-run with it in "next_cursor": "..." until has_more is false.

POST /discover/people

Discover people by title, department, company

Search the global people DB by title, department, management level, company, industry, skills, location, years of experience. Returns the same row shape your AI/list tools expect.

Request body
{
  "title": ["VP Sales", "Head of Sales"],
  "departments": ["Sales"],
  "management_levels": ["VP", "Director"],
  "company_name": ["Stripe", "Plaid"],
  "years_experience": ["6 to 10 years", "More than 10 years"],
  "limit": 25
}
Response
{
  "data": [
    {
      "id": 42818,
      "first_name": "Alice",
      "last_name": "Chen",
      "full_name": "Alice Chen",
      "title": "VP of Sales",
      "department": "Sales",
      "management_level": "VP",
      "linkedin_url": "https://www.linkedin.com/in/alice-chen-vp",
      "location": "San Francisco",
      "years_experience": "More than 10 years",
      "company": {
        "id": 8421,
        "name": "Stripe",
        "domain": "stripe.com"
      }
    }
  ],
  "count": 25,
  "total": 412,
  "next_cursor": "eyJjdXJzb3IiOiIuLi4ifQ",
  "has_more": true,
  "credit_charged": 1,
  "credits_remaining": 4820
}
  • Resolve exact `departments` and `management_levels` values via `GET /filters/departments` and `GET /filters/management-levels` first.
  • `years_experience` must use the exact bucket labels: "Less than 1 year", "1 to 2 years", "3 to 5 years", "6 to 10 years", "More than 10 years".
  • No email or phone in the response — chain into `/email/search` or `/phone/search` for that.
Wire it in Clay

Two patterns:

  • Find decision-makers at a list of companies — start with a company table, add HTTP API as enrichment, pass {{ company_name }} into company_name and the column filters from your row.
  • Build a people table from scratch — use "Use as source"; each row of $.data[*] becomes a person.
Clay body — per-row enrichment
{
  "title": ["VP Sales", "Head of Sales"],
  "departments": ["Sales"],
  "management_levels": ["VP", "Director"],
  "company_name": ["{{ company_name }}"],
  "limit": 10
}

Need more endpoints?

The recipes above are the most common flows — Oppora exposes 17 REST endpoints in total (discovery, bulk async jobs, filter helpers, account info). See the full REST API reference for the complete surface.