Play Video
n8n logo

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.

  1. In n8n, go to Credentials (left sidebar) → Create New.
  2. Search and pick Header Auth.
  3. Set:
    Header Auth credential
    Name:  Oppora API
    Header Name:  Authorization
    Header Value: Bearer YOUR_KEY
  4. Save. In any HTTP Request node, set Authentication Generic Credential TypeHeader 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/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 n8n

Method: POST. URL: https://api.oppora.ai/api/v1/public/email/search. Body Content Type: JSON. Specify Body: Using JSON.

JSON body (with n8n expressions)
{
  "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/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 n8n

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".

JSON body
{
  "email": "{{ $json.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 n8n

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 body — conditional shape
{{ $json.linkedin_url
   ? { linkedin_url: $json.linkedin_url }
   : { first_name: $json.first_name, last_name: $json.last_name, domain: $json.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 n8n

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.

JSON body — page 1
{
  "industry": ["Software Development"],
  "hq_country": "USA",
  "size": ["51-200", "201-500"],
  "limit": 100
}
JSON body — page N (cursor)
{
  "next_cursor": "{{ $json.next_cursor }}",
  "limit": 100
}
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 n8n

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.

JSON body — find VPs at a specific company
{
  "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.