Oppora in Make
Call Oppora from any Make scenario using the built-in HTTP module. Bearer auth goes in a single header, JSON bodies map directly to upstream module fields.
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.
- 3A Make account — the HTTP app is included on every plan, including Free.
Set up the HTTP module once, copy it
Make's HTTP module doesn't have reusable named connections the way n8n or Zapier do — every call carries its own header. The cleanest pattern is to build one module with the header configured, then right-click → Clone it into other scenarios so you don't re-type the bearer.
- In your scenario, click + → search HTTP → pick Make a request.
- Configure the request:HTTP module — base settings
Method: POST Headers: Name: Authorization Value: Bearer YOUR_KEY Body type: Raw Content type: JSON (application/json) Parse response: Yes
- Set the URL and body per recipe below.
- For repeat use across scenarios: right-click the module → Clone, or save the scenario as a template.
Tip: keep your key in Data Stores if you want a single source of truth — reference it as {{getValue("oppora_creds"; "api_key")}} in the header field. Useful when you rotate the key.
Recipes
Each recipe is one HTTP Make a request module. Reference fields from upstream modules with {{ 1.field_name }} (where 1. is the module number).
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.
URL: https://api.oppora.ai/api/v1/public/email/search. Paste the JSON body and replace literal values with field mappings from your trigger:
{
"first_name": "{{ 1.first_name }}",
"last_name": "{{ 1.last_name }}",
"domain": "{{ 1.domain }}"
}With Parse response: Yes, downstream modules can map email and status from the response object directly.
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 after email-search. Add a Router with two filters: status = valid → send, status = risky → second verify module with "mode": "advanced".
{
"email": "{{ 1.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.
Build a single module that prefers LinkedIn URL when present. Use Make's if() function in the body:
{
"linkedin_url": "{{ if(length(1.linkedin_url) > 0; 1.linkedin_url; emptystring) }}",
"first_name": "{{ if(length(1.linkedin_url) > 0; emptystring; 1.first_name) }}",
"last_name": "{{ if(length(1.linkedin_url) > 0; emptystring; 1.last_name) }}",
"domain": "{{ if(length(1.linkedin_url) > 0; emptystring; 1.domain) }}"
}Cleaner: split into two modules + a router on linkedin_url presence.
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, wrap the HTTP module in a Repeater + Aggregator pair, or use the Iterator on data to fan-out rows. Feed next_cursor back via a variable.
{
"industry": ["Software Development"],
"hq_country": "USA",
"size": ["51-200", "201-500"],
"limit": 100
}{
"next_cursor": "{{ 2.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 chain: Iterator over the company rows from discover_companies, then this module per-company. Add an Iterator on data after the call to fan-out each person into its own bundle.
{
"title": ["VP Sales", "Head of Sales"],
"departments": ["Sales"],
"management_levels": ["VP", "Director"],
"company_name": ["{{ 1.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.