Oppora in n8n
Wire Oppora into any n8n workflow with the built-in HTTP Request node. Save your API key as a Header Auth credential once, then reference it from every node in any workflow.
Before you start
- 1An Oppora API key (Integrations → Oppora API in the app). Starts with opp_live_…
- 2A paid Oppora plan (Pro or Max). API keys aren't available on Free.
- 3An n8n instance — Cloud or self-hosted. Both work identically.
Create one Header Auth credential, reuse everywhere
n8n's Header Auth credential type is exactly what we need — pastes the bearer token onto every request, reusable across all workflows. Set it up once.
- In n8n, go to Credentials (left sidebar) → Create New.
- Search and pick Header Auth.
- Set:Header Auth credential
Name: Oppora API Header Name: Authorization Header Value: Bearer YOUR_KEY
- Save. In any HTTP Request node, set Authentication → Generic Credential Type → Header Auth → pick
Oppora API.
Recipes
Each recipe is one HTTP Request node. Add the node, set authentication to your Oppora API credential, then paste the URL and body. Reference upstream fields with {{ $json.field_name }}.
POST /email/searchFind email by name + domain
Resolves a verified work email when you have a person's name and their company's website domain.
{
"first_name": "Alice",
"last_name": "Chen",
"domain": "stripe.com"
}{
"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.
Method: POST. URL: https://api.oppora.ai/api/v1/public/email/search. Body Content Type: JSON. Specify Body: Using JSON.
{
"first_name": "{{ $json.first_name }}",
"last_name": "{{ $json.last_name }}",
"domain": "{{ $json.domain }}"
}Add a Set node downstream to flatten $json.email and $json.status for the next step.
POST /email/verifyVerify an email
Check deliverability before sending. Use `mode: "advanced"` for catch-all domains where `standard` returns `risky` or `unknown`.
{
"email": "[email protected]",
"mode": "standard"
}{
"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.
Chain right after the email-finder node. Add an IF node on {{ $json.status === "risky" }} and route only risky rows through a second verify call with "mode": "advanced".
{
"email": "{{ $json.email }}",
"mode": "standard"
}POST /phone/searchLookup 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.
{
"linkedin_url": "https://www.linkedin.com/in/alice-chen-vp"
}{
"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.
LinkedIn URL gives the highest hit rate. If the upstream row may or may not have one, build the body with a JS expression:
{{ $json.linkedin_url
? { linkedin_url: $json.linkedin_url }
: { first_name: $json.first_name, last_name: $json.last_name, domain: $json.domain } }}POST /discover/companiesDiscover 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.
{
"industry": ["Software Development"],
"hq_country": "USA",
"size": ["51-200", "201-500"],
"revenue_range_min": 5000000,
"limit": 25
}{
"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").
For pagination, use n8n's Loop Over Items + If pattern: check $json.has_more, feed $json.next_cursor back into a second HTTP Request node's body until exhausted.
{
"industry": ["Software Development"],
"hq_country": "USA",
"size": ["51-200", "201-500"],
"limit": 100
}{
"next_cursor": "{{ $json.next_cursor }}",
"limit": 100
}POST /discover/peopleDiscover 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.
{
"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
}{
"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.
Common pattern: take output rows from discover_companies, run them through Loop Over Items, and call this for each company to find decision-makers. Combine with Set to flatten the company sub-object.
{
"title": ["VP Sales", "Head of Sales"],
"departments": ["Sales"],
"management_levels": ["VP", "Director"],
"company_name": ["{{ $json.name }}"],
"limit": 10
}Chain into /email/search + /phone/search to attach contact info — neither is in the discover response.
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.