Skip to content

API Authentication

Secure access to the OpenProspect API using API keys for external integrations or OAuth 2.0 for internal tools.


Which Authentication Method Should I Use?

Use Case Method Best For
Server-to-server integration API Keys External developers, automation, production systems
Web application (internal) OAuth 2.0 + PKCE Internal tools, admin dashboards
CI/CD pipelines API Keys Automated workflows, deployment scripts

For external developers: Use API Keys — they're simpler, more secure for server-to-server use, and don't require user interaction.


Quick Start: API Keys

1. Get Your API Key

Create an API key from the OpenProspect dashboard:

  1. Log in to app.openprospect.io
  2. Go to Settings > Developer > API Keys
  3. Click Create API Key
  4. Choose a preset (Orders, Read Only, Full Access, or Custom), set the Rate Limit Tier and Expiration, then save the key securely

Important: The full key is shown only once at creation time. Copy it immediately and store it securely.

Key Format:

  • Production: lnc_live_... (32-character base62)
  • Testing: lnc_test_... (32-character base62)

The prefix prevents accidental use of test keys in production.

2. Store Your API Key Securely

# Environment variable (recommended)
export OPENPROSPECT_API_KEY="lnc_live_A3mK9pX2vL8hQ5nR7zT1wY4jB6cE0dF"

# Or use a .env file (never commit to git!)
echo "OPENPROSPECT_API_KEY=lnc_live_..." >> .env

3. Make Your First API Call

curl -X GET "https://api.openprospect.io/api/v1/orders?limit=10" \
  -H "Authorization: Bearer ${OPENPROSPECT_API_KEY}" \
  -H "Content-Type: application/json"
import os
import httpx

api_key = os.getenv("OPENPROSPECT_API_KEY")

response = httpx.get(
    "https://api.openprospect.io/api/v1/orders",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    },
    params={"limit": 10}
)

orders = response.json()
print(f"Found {len(orders['orders'])} orders")
const apiKey = process.env.OPENPROSPECT_API_KEY;

const response = await fetch(
  "https://api.openprospect.io/api/v1/orders?limit=10",
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    }
  }
);

const orders = await response.json();
console.log(`Found ${orders.orders.length} orders`);
const apiKey = process.env.OPENPROSPECT_API_KEY!;

interface OrderListResponse {
  orders: Array<{ id: string; title: string; status: string }>;
  total: number;
}

const response = await fetch(
  "https://api.openprospect.io/api/v1/orders?limit=10",
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    }
  }
);

const orders: OrderListResponse = await response.json();
console.log(`Found ${orders.orders.length} orders`);
using System.Net.Http.Headers;

var apiKey = Environment.GetEnvironmentVariable("OPENPROSPECT_API_KEY");

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", apiKey);
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

var response = await client.GetAsync(
    "https://api.openprospect.io/api/v1/orders?limit=10");

var orders = await response.Content.ReadFromJsonAsync<OrderListResponse>();
Console.WriteLine($"Found {orders.Orders.Count} orders");

API Key Management

Revoking API Keys

Immediately deactivate a compromised or unused API key:

curl -X DELETE "https://api.openprospect.io/api/v1/api-keys/${KEY_ID}" \
  -H "Authorization: Bearer ${JWT_TOKEN}"

Note: API key revocation requires JWT authentication with admin:write scope. Keys are managed via the dashboard or admin API.

Effect is immediate — the key stops working instantly.

Rotation

API keys are long-lived credentials. To rotate:

  1. Create a new key with the same scopes via the dashboard
  2. Update your systems to use the new key
  3. Verify the new key works
  4. Revoke the old key

When to rotate:

  • Every 90 days (recommended minimum)
  • Immediately if compromised
  • When team members with access leave
  • Before decommissioning old systems

Scopes and Permissions

API keys use scope-based permissions for fine-grained access control.

Available Scopes

Scope Description Typical Use Case
companies:read Read company data and profiles Read-only integrations, CRM sync
companies:write Create and update company records Data enrichment services
prospects:read Read prospect data and contacts Lead management systems
prospects:write Create and update prospect records Contact enrichment
prospect_searches:read Read prospect searches (ICPs) Monitoring dashboards
prospect_searches:write Create and update prospect searches ICP configuration
job_search:read Read job postings and search results Job market analysis
job_search:write Create and manage job searches Hiring signal tracking
handelsregister:read Read German commercial register data Legal data lookup
webtech_intel:read Read website technology intelligence Tech stack analysis
delivery:read Read delivered companies and feedback data Delivery monitoring
delivery:write Update feedback on delivered companies Quality feedback loop
organization:read Read organization settings and data Account info
organization:write Update organization settings Account configuration
orders:read Read enrichment order status and results Order tracking, result retrieval
orders:write Create enrichment orders Enrichment workflow
blacklists:read Read company blacklists and entries Blacklist management
blacklists:write Create, update, and delete company blacklists Blacklist CRUD, assignment

Presets

When creating an API key, you can choose a preset to quickly configure scopes. These match the options in the dashboard:

Preset Scopes Description
Orders orders:read, orders:write Create enrichment/discovery orders, track status, retrieve results
Read Only orders:read, delivery:read, companies:read, prospects:read View orders, deliveries, companies, and prospects
Full Access All public scopes Complete API access for all endpoints
Custom (choose individually) Select specific scopes for your use case

Scope Enforcement

Insufficient scopes return 403 Forbidden:

{
  "code": "AUTHORIZATION_ERROR",
  "message": "Insufficient permissions. Required scopes: orders:write",
  "detail": {
    "required_scopes": ["orders:write"],
    "user_scopes": ["orders:read"]
  }
}

Example: Scope-Restricted Request

# This will fail with 403 if API key lacks 'orders:write' scope
response = httpx.post(
    "https://api.openprospect.io/api/v1/orders",
    headers={"Authorization": f"Bearer {api_key}"},
    json={
        "title": "Q1 Enrichment",
        "features": ["CONTACTS"],
        "companies": [{"company_name": "Example Corp"}]
    }
)

if response.status_code == 403:
    error = response.json()
    print(f"Missing scopes: {error['required_scopes']}")
    print(f"Your scopes: {error['user_scopes']}")
const response = await fetch(
  "https://api.openprospect.io/api/v1/orders",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      title: "Q1 Enrichment",
      features: ["CONTACTS"],
      companies: [{ company_name: "Example Corp" }]
    })
  }
);

if (response.status === 403) {
  const error = await response.json();
  console.log(`Missing scopes: ${error.required_scopes}`);
  console.log(`Your scopes: ${error.user_scopes}`);
}

Security Best Practices

1. Storage

DO:

  • Store in environment variables
  • Use secret management systems (AWS Secrets Manager, HashiCorp Vault, 1Password)
  • Encrypt at rest in configuration files
  • Use different keys for development, staging, and production

DON'T:

  • Hard-code in source code
  • Commit to version control (add *.env to .gitignore)
  • Share keys between team members
  • Log keys in plain text
  • Send keys via email or Slack

2. Scope Principle of Least Privilege

Only grant the minimum scopes needed:

# Good: Enrichment partner needs only order scopes
Scopes: orders:read, orders:write

# Bad: Overly permissive
Scopes: companies:write, delivery:write, prospects:write, orders:write

3. Environment Separation

Always use the correct environment:

# Development
OPENPROSPECT_API_KEY="lnc_test_..."
OPENPROSPECT_API_URL="http://localhost:8000/api/v1"

# Production
OPENPROSPECT_API_KEY="lnc_live_..."
OPENPROSPECT_API_URL="https://api.openprospect.io/api/v1"

Test keys (lnc_test_) work only against test environments. Production keys (lnc_live_) prevent accidental test key usage in production.


Error Handling

Common Authentication Errors

Status Error Code Cause Solution
401 AUTHENTICATION_ERROR Invalid, expired, or revoked key Check key, generate new one if needed
401 AUTHENTICATION_ERROR No Authorization header Add Authorization: Bearer <key> header
403 AUTHORIZATION_ERROR API key lacks required scope Use a key with the required scopes
429 rate_limit_exceeded Too many requests Implement exponential backoff

Security Note: All authentication failures return generic messages to prevent enumeration attacks. The API does NOT distinguish between "key doesn't exist" vs "key is revoked" vs "key is expired".

Error Handling Examples

import httpx
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10)
)
def make_api_request(endpoint: str, api_key: str):
    response = httpx.get(
        f"https://api.openprospect.io/api/v1/{endpoint}",
        headers={"Authorization": f"Bearer {api_key}"}
    )

    if response.status_code == 401:
        raise AuthenticationError("Authentication failed. Check your API key.")

    if response.status_code == 403:
        error = response.json()
        raise PermissionError(
            f"Insufficient permissions. Required: {error['detail']['required_scopes']}"
        )

    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        raise RateLimitError(f"Rate limited. Retry after {retry_after}s")

    response.raise_for_status()
    return response.json()
async function makeAPIRequest(endpoint, apiKey) {
  const response = await fetch(
    `https://api.openprospect.io/api/v1/${endpoint}`,
    {
      headers: {
        "Authorization": `Bearer ${apiKey}`
      }
    }
  );

  if (response.status === 401) {
    throw new Error("Authentication failed. Check your API key.");
  }

  if (response.status === 403) {
    const error = await response.json();
    throw new Error(
      `Insufficient permissions. Required: ${error.detail.required_scopes}`
    );
  }

  if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After") || "60";
    throw new Error(`Rate limited. Retry after ${retryAfter}s`);
  }

  if (!response.ok) {
    throw new Error(`API error: ${response.statusText}`);
  }

  return await response.json();
}
class APIError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public errorCode?: string
  ) {
    super(message);
  }
}

async function makeAPIRequest<T>(
  endpoint: string,
  apiKey: string
): Promise<T> {
  const response = await fetch(
    `https://api.openprospect.io/api/v1/${endpoint}`,
    {
      headers: {
        "Authorization": `Bearer ${apiKey}`
      }
    }
  );

  if (response.status === 401) {
    throw new APIError(
      "Authentication failed. Check your API key.",
      401,
      "AUTHENTICATION_ERROR"
    );
  }

  if (response.status === 403) {
    const error = await response.json();
    throw new APIError(
      `Insufficient permissions. Required: ${error.detail.required_scopes}`,
      403,
      "AUTHORIZATION_ERROR"
    );
  }

  if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After") || "60";
    throw new APIError(
      `Rate limited. Retry after ${retryAfter}s`,
      429,
      "rate_limit_exceeded"
    );
  }

  if (!response.ok) {
    throw new APIError(`API error: ${response.statusText}`, response.status);
  }

  return await response.json();
}
public class APIClient
{
    private readonly HttpClient _client;

    public async Task<T> MakeAPIRequest<T>(string endpoint, string apiKey)
    {
        _client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", apiKey);

        var response = await _client.GetAsync(
            $"https://api.openprospect.io/api/v1/{endpoint}");

        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            throw new AuthenticationException(
                "Authentication failed. Check your API key.");
        }

        if (response.StatusCode == HttpStatusCode.Forbidden)
        {
            throw new UnauthorizedAccessException(
                "Insufficient permissions. Check your API key scopes.");
        }

        if (response.StatusCode == HttpStatusCode.TooManyRequests)
        {
            var retryAfter = response.Headers.RetryAfter?.Delta?.TotalSeconds ?? 60;
            throw new HttpRequestException(
                $"Rate limited. Retry after {retryAfter}s");
        }

        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>();
    }
}

Migration: OAuth to API Keys

If you're currently using OAuth/JWT tokens and want to switch to API keys:

Key Differences

Feature OAuth/JWT API Keys
Lifetime 1 hour (auto-refreshed) Long-lived (manual rotation)
Refresh Automatic with refresh token No refresh — manually rotate
Use Case User-facing applications Server-to-server integrations
Headers Authorization: Bearer <jwt> Authorization: Bearer lnc_live_...
Scopes All internal scopes Restricted external scopes

Migration Steps

  1. Create API key with required scopes via the dashboard
  2. Update authentication code — headers remain the same format
  3. Remove refresh logic — not needed for API keys
  4. Implement rotation schedule — every 90 days minimum
  5. Test thoroughly before revoking OAuth credentials

Internal Authentication (OAuth 2.0 + PKCE)

For internal tools only — External developers should use API keys.

The OpenProspect admin dashboard uses OAuth 2.0 with PKCE for user authentication. OAuth 2.0 is available for internal tool development. For external integrations, API keys are the recommended authentication method.


Next Steps

With authentication configured:

  1. Enrichment Quick Start — Create your first enrichment order in 5 minutes
  2. Enrichment Order Guide — Full enrichment workflow reference
  3. Discovery Quick Start — Create a blacklist and discovery order in 5 minutes
  4. Discovery Order Guide — ICP-based company discovery
  5. Blacklist Management Guide — Manage company exclusion lists
  6. Error Reference — Handle API errors gracefully

Tips

  1. Test keys first: Use lnc_test_ keys against test environments before production
  2. Rotate regularly: Rotate keys every 30-90 days
  3. Use minimal scopes: Grant only what's needed (e.g., orders:read + orders:write for enrichment)
  4. Separate keys per service: Don't share one key across multiple services
  5. Version your integration: Include version in User-Agent header

Questions? Check our Error Reference or contact support.