Download the PHP package jcolombo/leadfeeder-api-php without Composer
On this page you can find all versions of the php package jcolombo/leadfeeder-api-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download jcolombo/leadfeeder-api-php
More information about jcolombo/leadfeeder-api-php
Files in jcolombo/leadfeeder-api-php
Package leadfeeder-api-php
Short Description PHP SDK for the Leadfeeder website visitor tracking and lead generation API
License MIT
Homepage https://github.com/jcolombo/leadfeeder-api-php
Informations about the package leadfeeder-api-php
Leadfeeder API for PHP
Overview
This independently developed package provides a developer-friendly PHP toolkit for interacting with the Leadfeeder API. It is not affiliated with or endorsed by Leadfeeder / Dealfront.
Leadfeeder Homepage: https://www.leadfeeder.com API Documentation: https://docs.leadfeeder.com/api/
Stability Notice: This package is in active development (v0.x-alpha). The API surface may change before v1.0. Pin to
^0.xin production.
Features
- Read-Only Resource Access — Fetch and list Accounts, Leads, Visits, Custom Feeds, and Website Tracking Scripts
- Fluent Interface — Chainable methods for clean, readable code
- JSON:API Parsing — Automatic envelope parsing with include resolution (1–2 levels)
- Date Range Filtering — Built-in
dateRange()for temporal queries (required for Lead/Visit lists) - Smart Query Building — Server-side WHERE filters and client-side HAS post-filters
- Relationship Includes — Resolve Location entities on Lead/Visit responses
- Custom Feed Scoping — Filter leads by custom feed with
forFeed() - Lead-Scoped Visits — Retrieve visits for a specific lead with
forLead() - Export Manager — Async create/poll/download lifecycle for bulk lead export
- IP Enrichment — Identify companies by IP address via the Leadfeeder Discover API
- Multi-Scope Rate Limiting — Four independent sliding windows (per-token, per-account, export, IP-Enrich)
- Auto-Pagination —
fetchAll()iterates through all pages (10,000-lead cap for leads) - Response Caching — Built-in file-based caching with custom backend support
- Request Logging — Conditional file-based logging for debugging
- Type Coercion — Automatic property type conversion with extended type system
- Zero Dev Dependencies — Custom test framework requires no PHPUnit or dev packages
Requirements
- PHP 8.1 or higher
- A Leadfeeder account with API access
- Your Leadfeeder API token (from Settings → Personal → API Tokens)
- Composer
- (Optional) A Leadfeeder Discover API key for IP enrichment
Installation
The package is published on Packagist and follows standard PSR-4 autoloading under the Jcolombo\LeadfeederApiPhp namespace. No additional configuration is required to get started — sensible defaults are loaded automatically from the bundled default.leadfeederapi.config.json.
Quick Start
Connecting and Selecting an Account
Authentication is Bearer token–based. Call Leadfeeder::connect() once with your token. The
connection is cached as a singleton, so calling it again with the same token returns the
same instance. Most API endpoints are scoped to a specific account ID, which you provide via
setAccount().
Fetching Leads
Lead list requests require a date range. The dateRange() method sets the start_date and
end_date server-side filters in a single call. Without a date range the Leadfeeder API will
return an error; in devMode the SDK emits a warning before the request is sent.
Fetching Visits
Visit lists are also date-range-required. You can iterate over a collection directly with a
foreach loop because collections implement the Iterator interface.
Supported Resources
The SDK models six Leadfeeder API entities. Each resource class lives under
Jcolombo\LeadfeederApiPhp\Entity\Resource\.
| Resource | Class | Scope | Pattern | Notes |
|---|---|---|---|---|
| Account | Account |
Token | list(), fetch($id) |
Lists all accounts for the token |
| Lead | Lead |
Account | list(), fetch($id) |
Date range required for list |
| Visit | Visit |
Account | list(), fetch($id) |
Date range required for list |
| CustomFeed | CustomFeed |
Account | list(), fetch($id) |
Read-only feed definitions |
| Location | Location |
Include-only | Resolved via include | Cannot be fetched or listed directly |
| WebsiteTrackingScript | WebsiteTrackingScript |
Account | fetch() (singleton) |
list() throws RuntimeException |
Account requests are token-scoped (no account ID needed). All other requests are automatically
prefixed with accounts/{accountId}/ when an account has been set on the connection. The
Location entity is resolved only as a relationship include on Lead and Visit responses — it has
no standalone API endpoint.
Date Range Filtering
The Leadfeeder API requires start_date and end_date parameters on Lead and Visit list
requests. Omitting them will cause the API to return an error. The SDK provides the dateRange()
fluent method as a first-class concept to make this explicit and convenient.
Dates must be formatted as YYYY-MM-DD strings. Both start and end are inclusive.
devMode warning: When
devModeis enabled in configuration, the SDK emits aWARN-level error if you callfetch()orfetchAll()on a Lead or Visit collection without first callingdateRange(). It does not throw an exception, so the request still proceeds — but the API will likely return an error response.
Pagination
The Leadfeeder API uses 1-indexed, page-number-based pagination with a configurable page size.
The SDK default page size is 100. Individual requests return a single page; fetchAll() continues
fetching until the links.next key is absent from the API response.
Fetching a Single Page
Controlling Page Number and Page Size
Auto-Pagination with fetchAll()
The fetchAll() method iterates through all available pages automatically, accumulating results
into a single collection. For Lead collections, auto-pagination stops at 10,000 records regardless
of whether more pages exist — this cap prevents runaway memory consumption on large accounts.
Key pagination facts:
- Pages are 1-indexed. Passing
page(0)will result in unexpected API behavior. - The SDK default page size is 100. The Leadfeeder API maximum is typically 1,000.
fetchAll()stops whenlinks.nextis absent from the API response or the 10,000-lead cap is reached.- Visit collections use the base
fetchAll()with no hard cap; only LeadCollection enforces 10,000.
Query Building
The SDK separates filtering into two complementary strategies: server-side WHERE filters that are sent as query parameters in the API request, and client-side HAS filters that are evaluated after the response is received. Understanding which approach to use for a given condition is important for both correctness and performance.
WHERE Filters (Server-Side)
Server-side filters are passed as query parameters and evaluated by the Leadfeeder API. Only the
fields explicitly listed in each resource's WHERE_OPERATIONS constant are valid server-side
filter keys.
Supported server-side filter fields by resource:
| Resource | Field | Operator |
|---|---|---|
| Lead | start_date |
= |
| Lead | end_date |
= |
| Lead | custom_feed_id |
= |
| Visit | start_date |
= |
| Visit | end_date |
= |
Note that start_date and end_date are set automatically by dateRange() — you do not need
to pass them via where() directly. Using where() for dates is supported but redundant when
dateRange() is already called.
devMode warning: When
devModeis enabled, callingwhere()with a field that is not in the resource'sWHERE_OPERATIONStable emits aWARN-level error. This helps catch typos and unsupported filters during development.
HAS Filters (Client-Side)
Client-side HAS filters are evaluated in PHP after the API response is received. They can match
against any property in the resource's PROP_TYPES definition, not just the fields supported as
server-side query parameters. This makes has() useful for any narrowing that the API does not
natively support.
Supported has() operators:
| Operator | Meaning |
|---|---|
= |
Equal (default) |
!= |
Not equal |
> |
Greater than |
>= |
Greater than or equal |
< |
Less than |
<= |
Less than or equal |
like |
Case-insensitive substring match |
The like operator uses PHP's str_contains() after lowercasing both sides. It is only valid
for string properties; numeric properties use the comparison operators.
Relationship Includes
The Leadfeeder API supports sideloading related entities via the include query parameter. The
SDK maps these to PHP entity objects automatically through its JSON:API parser. Currently, the
only supported relationship include is locations on both Lead and Visit responses.
Including Locations on Leads
Including Locations on Visits
The Location entity is include-only — it has no standalone API path and cannot be fetched or
listed directly. Calling Location::fetch() or Location::list() will throw a
RuntimeException. Included Location objects expose the following properties: id, country,
country_code, region, region_code, city, state_code.
Custom Feed Scoping
Custom Feeds are saved filter configurations in Leadfeeder that segment your leads into named
groups. You can list all custom feeds to discover their IDs, then use forFeed() on a Lead
collection to retrieve leads scoped to that feed. This sets a parent context that prefixes the
API request path with custom-feeds/{feedId}/.
Listing Custom Feeds
Fetching Leads for a Specific Feed
forFeed() and the server-side where('custom_feed_id', ...) filter are complementary
approaches to the same goal. forFeed() uses a nested URL path; where() passes the feed ID
as a query parameter. In most cases forFeed() is the cleaner choice.
Lead-Scoped Visits
You can retrieve all visits attributed to a specific lead by calling forLead() on a Visit
collection. This sets the parent context to leads/{leadId}/, scoping the API path to
accounts/{accountId}/leads/{leadId}/visits.
The visit_route property is typed as array:object, meaning it is an array of associative
arrays. Each element represents a single page view within the visit session.
Website Tracking Script (Singleton)
The Website Tracking Script is a singleton resource — there is exactly one per account, and it
has no list endpoint. Fetch it by calling fetch() with no arguments. Calling list() on this
resource will throw a RuntimeException.
Available properties: id, script_hash, script_html (typed html), timezone.
The
list()method onWebsiteTrackingScriptis disabled and throws:RuntimeException: WebsiteTrackingScript is a singleton entity and cannot be listed.
Export Workflow
The Leadfeeder export system is asynchronous. You create an export job, poll for completion,
and then download the processed data once it is ready. The ExportManager class handles this
full lifecycle.
Full Lifecycle: Create, Wait, Download
Manual Polling Alternative
If you need finer control over the polling interval or want to integrate the status check into
your own event loop, use checkStatus() directly.
Filtering by Custom Feed
Pass custom_feed_id in the params array to scope the export to a specific feed.
Required parameters: account_id, start_date, end_date. custom_feed_id is optional.
The export creation POST request uses the export rate-limit scope (5 requests/minute). Status
polls use the per-token rate limit scope. Download requests use an unauthenticated client — the
pre-signed download URL itself serves as the credential.
IP Enrichment
The Leadfeeder Discover API lets you identify company information for a given IP address. This is
a separate API service with its own endpoint (https://api.lf-discover.com) and authentication
method (X-API-KEY header). It requires a distinct API key separate from your Leadfeeder token.
The IpEnrichClient can be created directly or via the Leadfeeder::connectIpEnrich() factory,
which caches clients by API key.
Key points about IP Enrichment:
- Separate API key — your Leadfeeder token does not work with the Discover API
X-API-KEYauthentication — the Discover API uses a different auth header than the main API- Returns raw arrays —
lookup()returns?array, not an entity object - 404 = null — when no company is found for the IP,
lookup()returnsnullwithout raising an error - Independent rate limit — 60 requests per minute (configurable), tracked in its own sliding window
- No connection required —
IpEnrichClientdoes not need aLeadfeederconnection instance
Configuration
The SDK ships with a default.leadfeederapi.config.json file that contains all default values.
You can override any setting by loading a custom configuration file or by calling Configuration::set()
at runtime.
Default Configuration
By default, the SDK connects to https://api.leadfeeder.com, disables caching and logging, and
enables rate limiting with a 100-requests-per-minute general limit and 5-per-minute for exports.
Custom Configuration File
Create a leadfeederapi.config.json file in your project and load it at bootstrap. Only the keys
you wish to override need to be present — values are merged recursively with the defaults.
Loading Configuration
overload() looks for a file named leadfeederapi.config.json in the given directory (or uses
the path directly if it points to a file). If the file does not exist, it silently returns.
load() requires the file to exist and throws on invalid JSON.
Configuration Options
| Key | Type | Default | Description |
|---|---|---|---|
connection.url |
string | https://api.leadfeeder.com |
Base API URL |
connection.timeout |
int | 30 |
HTTP request timeout in seconds |
connection.verify |
bool | true |
SSL certificate verification |
ipEnrich.url |
string | https://api.lf-discover.com |
Discover API base URL |
ipEnrich.rateLimit.perMinute |
int | 60 |
IP Enrichment rate limit |
enabled.cache |
bool | false |
Enable response caching |
enabled.logging |
bool | false |
Enable request logging |
rateLimit.enabled |
bool | true |
Enable rate limiting |
rateLimit.perMinute |
int | 100 |
General requests per minute |
rateLimit.export.perMinute |
int | 5 |
Export creation requests per minute |
rateLimit.minDelayMs |
int | 200 |
Minimum milliseconds between requests |
rateLimit.safetyBuffer |
int | 1 |
Subtract from perMinute before throttling |
rateLimit.maxRetries |
int | 3 |
Max 429 retry attempts |
rateLimit.retryDelayMs |
int | 2000 |
Initial retry delay in milliseconds |
devMode |
bool | false |
Enable development warnings |
log.connections |
bool | false |
Log new connection creation |
log.requests |
bool | true |
Log each HTTP request |
error.enabled |
bool | true |
Enable error handling |
error.triggerPhpErrors |
bool | false |
Trigger native PHP errors |
error.handlers.notice |
array | ["log"] |
Handlers for notice-level errors |
error.handlers.warn |
array | ["log"] |
Handlers for warning-level errors |
error.handlers.fatal |
array | ["log", "echo"] |
Handlers for fatal errors |
Caching
The SDK includes a built-in file-based response cache for GET requests. Caching is disabled by
default and must be explicitly enabled. Once enabled, successful GET responses are serialized and
stored on disk; subsequent identical requests are served from cache within the configured lifespan.
POST requests (such as export creation) automatically invalidate related cache entries via
ScrubCache.
Option A: Enable via Configuration
Option B: PHP Constant
Define the LFAPI_REQUEST_CACHE_PATH constant before making any requests. The SDK looks for this
constant to determine the cache directory. Both the constant and the enabled.cache config key
must be set for caching to activate.
Cache files are written to a lfapi-cache/ subdirectory inside the configured path. The default
lifespan is 300 seconds (5 minutes). Files older than the lifespan are deleted on the next access
attempt.
Cache Behavior Summary
- Only
GETrequests are cached POSTrequests (export create) trigger cache invalidation for the related URL scope- Cached responses are stored as serialized
RequestResponseobjects - Cache hits return a new
RequestResponsewith thefromCacheKeyproperty populated
Custom Cache Backend
If you need Redis, Memcached, or any other storage layer, register three callables with
Cache::registerCacheMethods():
Rate Limiting
The SDK implements multi-scope rate limiting with sliding window tracking. Each scope maintains an independent timestamp log, allowing fine-grained control over different request categories without one scope blocking another.
Four Rate Limit Scopes
| Scope Key | Applies To | Default Limit |
|---|---|---|
token:{hash} |
All requests using a specific token (no account set) | 100/min |
account:{id} |
All requests once setAccount() is called |
100/min |
export |
ExportManager::create() POST requests only |
5/min |
ipenrich:{hash} |
IpEnrichClient::lookup() requests |
60/min |
How Rate Limiting Works
Before each request, RateLimiter::waitIfNeeded() runs a three-stage check:
- Prune all timestamps older than 60 seconds from the sliding window
- If the number of recent requests has reached
perMinute - safetyBuffer, sleep until the oldest timestamp in the window ages out of the 60-second window - If the time elapsed since the last request is less than
minDelayMs, sleep the remaining gap
On a 429 response from the API, the SDK retries up to maxRetries times (default 3) with
exponential backoff starting at retryDelayMs (default 2,000ms). After exhausting all retries,
a FATAL error is raised.
Rate limiting can be disabled entirely for testing or high-trust environments:
Error Handling
The SDK uses a three-level severity system for all internal error conditions. Error behavior is fully configurable — you can choose whether errors are logged, echoed, or trigger native PHP errors, independently per severity level.
Severity Levels
| Level | Enum Case | Default Handlers | Typical Cause |
|---|---|---|---|
notice |
ErrorSeverity::NOTICE |
log |
Informational — non-critical conditions |
warn |
ErrorSeverity::WARN |
log |
Potential problem — missing date range in devMode |
fatal |
ErrorSeverity::FATAL |
log, echo |
Unrecoverable — rate limit exhausted, 402, 5xx |
Customizing Error Behavior
Checking Response Success
Every request returns a RequestResponse object. Always check $response->success before
processing the body. The entity and collection classes handle this internally, but when you need
raw access:
In normal SDK usage — through resource fetch(), list()->fetch(), or ExportManager — errors
are surfaced automatically through the configured error handlers. You do not need to unwrap
response objects manually unless you are extending the SDK.
Working with Properties
Every resource entity exposes its properties through magic __get / __set accessors and the
explicit get() / set() methods. Properties are automatically coerced to their defined types
upon hydration.
Magic Property Access
Complex Type Examples
Some properties are typed as complex structures rather than scalar values.
Property Types
The SDK's type system covers all Leadfeeder data shapes:
| Type | PHP Representation | Example Properties |
|---|---|---|
text |
string |
name, website_url, id |
integer |
int |
employee_count, quality, visits |
date |
string (YYYY-MM-DD) |
first_visit_date, last_visit_date |
datetime |
string (ISO 8601) |
started_at |
array |
array (flat) |
tags, ga_client_ids |
object |
array (assoc) |
employees_range |
array:object |
array of array |
industries, visit_route |
html |
string (raw HTML) |
script_html |
enum:* |
string (validated) |
subscription, website_tracking_status |
Serialization
Advanced Usage
Multiple Connections
The Leadfeeder::connect() factory is a singleton keyed by token. If you need to work with
multiple API tokens simultaneously, call connect() with each distinct token — each returns its
own independent connection with its own rate limit state.
Combining Web Visitor Leads with IP Enrichment
A common pattern is to retrieve leads from the web visitor feed and then enrich any leads that have a known IP address with additional company data from the Discover API.
Running Tests
The SDK ships with a custom zero-dependency test runner. No PHPUnit or other test framework is
required. The test suite makes live API calls and requires valid credentials configured in the
testing section of the config.
Running the Suite
CLI Options
The test runner script accepts the following options directly:
| CLI Option | Effect |
|---|---|
--dry-run |
Parse and display the test plan without executing any requests |
--verbose |
Print each individual assertion result as it runs |
--resource=<name> |
Run only the test group for the named resource (e.g. lead, visit, account) |
Test Credentials
Supply your API credentials via the testing block in a local leadfeederapi.config.json file.
This file should never be committed to version control.
Then load the file before running tests, or place it in the project root where
Configuration::overload() will pick it up automatically.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md before submitting a
pull request. The document covers branching conventions, the PR workflow, coding standards
(PSR-12, PHP 8.1 minimum, strict_types), and the changelog maintenance requirement.
License
MIT — see LICENSE for details.
Credits
Developed and maintained by Joel Colombo at 360 PSG, Inc.
This package is independently developed and is not affiliated with or endorsed by Leadfeeder or Dealfront.
Changelog
See CHANGELOG.md for a detailed history of changes.
All versions of leadfeeder-api-php with dependencies
ext-json Version *
guzzlehttp/guzzle Version ^7.8
adbario/php-dot-notation Version ^3.3