curl --request POST \
--url https://api.enrichloops.com/v1/people/enrich \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"linkedinUrl": "https://www.linkedin.com/in/jane-doe",
"email": "jane.doe@acme.com",
"fullName": "Jane Marie Doe",
"title": "Head of Finance",
"location": "Amsterdam",
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://api.enrichloops.com/v1/people/enrich"
payload = {
"linkedinUrl": "https://www.linkedin.com/in/jane-doe",
"email": "jane.doe@acme.com",
"fullName": "Jane Marie Doe",
"title": "Head of Finance",
"location": "Amsterdam",
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
linkedinUrl: 'https://www.linkedin.com/in/jane-doe',
email: 'jane.doe@acme.com',
fullName: 'Jane Marie Doe',
title: 'Head of Finance',
location: 'Amsterdam',
loop_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://api.enrichloops.com/v1/people/enrich', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.enrichloops.com/v1/people/enrich",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'linkedinUrl' => 'https://www.linkedin.com/in/jane-doe',
'email' => 'jane.doe@acme.com',
'fullName' => 'Jane Marie Doe',
'title' => 'Head of Finance',
'location' => 'Amsterdam',
'loop_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.enrichloops.com/v1/people/enrich"
payload := strings.NewReader("{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.enrichloops.com/v1/people/enrich")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.enrichloops.com/v1/people/enrich")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"task": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object_type": "company",
"operation": "enrich",
"status": "queued",
"target": "<string>",
"credits_charged": 123,
"created_at": "2023-11-07T05:31:56Z",
"input": {},
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"queued_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
},
"error": {
"code": "not_found",
"message": "<string>",
"candidates": 123,
"hintsAccepted": [
"<string>"
]
}
}{
"task": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object_type": "company",
"operation": "enrich",
"status": "queued",
"target": "<string>",
"credits_charged": 123,
"created_at": "2023-11-07T05:31:56Z",
"input": {},
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"queued_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
},
"error": {
"code": "not_found",
"message": "<string>",
"candidates": 123,
"hintsAccepted": [
"<string>"
]
}
}{
"error": "<string>"
}"Unauthorized"{
"error": "<string>",
"code": "credits_expired",
"creditsRemaining": 123
}{
"error": "<string>",
"code": "idempotency_key_reused"
}{
"error": "<string>",
"code": "queue_full",
"queuedTasks": 123
}{
"error": "<string>",
"requestId": "<string>"
}Enrich a person
Enqueues an enrichment for a person, identified by LinkedIn profile
URL or by work email address. Provide exactly one of linkedinUrl or
email.
Responds 200 if the task settled inside the request (a person we
already hold), or 202 if it was queued. Both responses share the same
shape. The body never carries the result, only the task. Poll GET /v1/tasks/{id} or supply loop_id to get the result delivered to a
Loop’s webhook.
The two inputs take different routes. A profile URL is an address we can fetch. If we do not already hold that person, the task queues, pulls their profile, and returns it.
An email address is resolved live (reverse email lookup). We identify
the company behind the domain, work out which names the local part
could describe, search that company’s staff on LinkedIn, pull each
plausible profile, and confirm the person holds a current position
there under a name the address fits. Only a confirmed person is
returned. The result carries an email_match object: method is
"exact" for an address already on record, "resolved" for one
confirmed against a profile pulled for this request, "inferred" for
one recognized among people we already held.
When we are not sure, we say so and do not charge. If the address
fits more than one current employee and nothing separates them, the
task fails with ambiguous. error carries candidates (how many)
and hintsAccepted. Retry the same email with one of fullName,
title or location and we break the tie against the profiles we
already gathered. A miss is not_found. Both are refunded. An address
we cannot confidently resolve never costs you a credit.
Personal addresses (gmail, outlook, …) and role addresses (info@,
sales@) are refused as not_found without any lookup.
An email lookup costs 3 credits. A URL lookup costs 1. Resolving an address can take several LinkedIn retrievals.
Freshness. A stored profile older than 30 days is not served as-is.
The request is queued (202) and the profile is re-fetched before the
task settles. This matters more for people than for companies. A
person who changed jobs is not just out of date, and the new role is
usually the reason you asked. If the refresh cannot complete, the task
still succeeds and returns the stored record. Compare
last_enriched_at to tell the two apart. Email lookups follow the same
rule: an address that resolves to a person we already hold is
refreshed the same way.
curl --request POST \
--url https://api.enrichloops.com/v1/people/enrich \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"linkedinUrl": "https://www.linkedin.com/in/jane-doe",
"email": "jane.doe@acme.com",
"fullName": "Jane Marie Doe",
"title": "Head of Finance",
"location": "Amsterdam",
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://api.enrichloops.com/v1/people/enrich"
payload = {
"linkedinUrl": "https://www.linkedin.com/in/jane-doe",
"email": "jane.doe@acme.com",
"fullName": "Jane Marie Doe",
"title": "Head of Finance",
"location": "Amsterdam",
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
linkedinUrl: 'https://www.linkedin.com/in/jane-doe',
email: 'jane.doe@acme.com',
fullName: 'Jane Marie Doe',
title: 'Head of Finance',
location: 'Amsterdam',
loop_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://api.enrichloops.com/v1/people/enrich', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.enrichloops.com/v1/people/enrich",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'linkedinUrl' => 'https://www.linkedin.com/in/jane-doe',
'email' => 'jane.doe@acme.com',
'fullName' => 'Jane Marie Doe',
'title' => 'Head of Finance',
'location' => 'Amsterdam',
'loop_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.enrichloops.com/v1/people/enrich"
payload := strings.NewReader("{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.enrichloops.com/v1/people/enrich")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.enrichloops.com/v1/people/enrich")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"linkedinUrl\": \"https://www.linkedin.com/in/jane-doe\",\n \"email\": \"jane.doe@acme.com\",\n \"fullName\": \"Jane Marie Doe\",\n \"title\": \"Head of Finance\",\n \"location\": \"Amsterdam\",\n \"loop_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"task": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object_type": "company",
"operation": "enrich",
"status": "queued",
"target": "<string>",
"credits_charged": 123,
"created_at": "2023-11-07T05:31:56Z",
"input": {},
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"queued_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
},
"error": {
"code": "not_found",
"message": "<string>",
"candidates": 123,
"hintsAccepted": [
"<string>"
]
}
}{
"task": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object_type": "company",
"operation": "enrich",
"status": "queued",
"target": "<string>",
"credits_charged": 123,
"created_at": "2023-11-07T05:31:56Z",
"input": {},
"loop_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"queued_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"finished_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
},
"error": {
"code": "not_found",
"message": "<string>",
"candidates": 123,
"hintsAccepted": [
"<string>"
]
}
}{
"error": "<string>"
}"Unauthorized"{
"error": "<string>",
"code": "credits_expired",
"creditsRemaining": 123
}{
"error": "<string>",
"code": "idempotency_key_reused"
}{
"error": "<string>",
"code": "queue_full",
"queuedTasks": 123
}{
"error": "<string>",
"requestId": "<string>"
}Authorizations
Pass your API key as a bearer token: Authorization: Bearer YOUR_API_KEY. Generate and manage keys from your dashboard under
Settings → API Keys.
Headers
Reusing the same key returns the original task instead of creating and charging a new one. Use it to retry a request whose response you never received.
A key is bound to the body it was first sent with. Reusing it with
a different body is rejected with 422. Maximum 255 characters.
255Body
A LinkedIn profile URL, e.g. "https://www.linkedin.com/in/jane-doe".
"https://www.linkedin.com/in/jane-doe"
A work email address, as an alternative to linkedinUrl.
Resolved live against LinkedIn. See the endpoint
description for what happens on a miss or a tie.
"jane.doe@acme.com"
Optional hint for email lookups only. Read solely to
break a tie after an ambiguous result. Never used as a
search filter. Rejected alongside linkedinUrl.
200"Jane Marie Doe"
Optional tie-break hint for email lookups. The person's job title.
200"Head of Finance"
Optional tie-break hint for email lookups. A city or country.
200"Amsterdam"
Deliver the result to this Loop's webhook once it's ready, in addition to polling this task directly.
Response
The task settled inside the request (a record we already hold, or an idempotent replay).
Never carries data. Carries error only if the task already failed
inside the request. Fetch the result from GET /v1/tasks/{id} or a
Loop webhook.