Pipedrive API V1 to V2 Migration Guide for Developers
The complete technical reference covering every breaking change between Pipedrive API V1 and V2 — with code examples, before/after comparisons, and a migration checklist.
Overview
Pipedrive is migrating from API V1 to V2 — a major overhaul that improves performance, enforces REST best practices, and restructures how data is returned. If you've built any integration that talks to Pipedrive's API, you need to update your code.
This guide covers every breaking change we've documented while building migration tools for thousands of Pipedrive integrations. It's based on real-world migration experience — not just the changelog.
Timeline & Deadline
V1 API Retirement: July 31, 2026
All V1 endpoints will stop responding after this date. V2 endpoints are already available for Deals, Persons, Organizations, Activities, Products, Pipelines, Stages, Search, and Fields.
Leads API remains V1-only — Pipedrive has not announced a V2 Leads endpoint as of March 2026. Notes also lack a native V2 endpoint (use the generic POST /api/v2/notes via the API directly).
Base URL Changes
The V2 base path includes an additional /api/ segment:
Common Mistake
Using /v2/deals instead of /api/v2/deals returns a 404. The /api/ prefix is required for all V2 endpoints.
Authentication
V2 changes how API tokens are passed:
API Token
V1 allowed ?api_token=xxx in the URL. V2 does not — you must use the x-api-token header:
# V1 — query string (no longer works)
curl "https://api.pipedrive.com/v1/deals?api_token=YOUR_TOKEN"
# V2 — header required
curl -H "x-api-token: YOUR_TOKEN" \
"https://api.pipedrive.com/api/v2/deals"
OAuth 2.0
OAuth tokens continue to use the standard Authorization: Bearer header:
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://api.pipedrive.com/api/v2/deals"
Don't Mix Auth Methods
Using Authorization: Bearer with an API token (not an OAuth token) returns 401. API tokens must use x-api-token.
HTTP Method Changes
| Operation | V1 Method | V2 Method |
|---|---|---|
| Update entity | PUT | PATCH |
| Delete entity | PUT (set flag) or DELETE | DELETE only |
| Create entity | POST | POST (unchanged) |
V2 enforces proper REST semantics. You can no longer soft-delete an entity with PATCH/PUT — use the DELETE endpoint.
Custom Fields — The Biggest Breaking Change
This is the most impactful change in V2. Custom fields are completely restructured in both requests and responses.
Reading Custom Fields
In V1, custom fields appear flat on the entity object. In V2, they're nested under custom_fields:
// V1 Response — flat
{
"id": 123,
"title": "Big Deal",
"abc123def456_hash_key": "custom value",
"abc123def456_hash_key_currency": "USD"
}
// V2 Response — nested under custom_fields
{
"id": 123,
"title": "Big Deal",
"custom_fields": {
"abc123def456_hash_key": {
"value": "custom value",
"currency": "USD"
}
}
}
Requesting Custom Fields
V2 excludes custom fields by default. You must explicitly request them:
# Request specific custom fields
GET /api/v2/deals/123?custom_fields=hash_key_1,hash_key_2
# Request all custom fields (note: max 15 per request)
GET /api/v2/deals/123?include_fields=custom_fields
Writing Custom Fields
When creating or updating entities, custom fields must be wrapped in a custom_fields object:
// V1 — flat body
{
"title": "New Deal",
"abc123_hash": "my value"
}
// V2 — nested body
{
"title": "New Deal",
"custom_fields": {
"abc123_hash": "my value"
}
}
Complex Field Types
Some custom field types require object-wrapped values:
| Type | V2 Write Format | V2 Read Format |
|---|---|---|
| text, number, date, enum | "hash": "value" | "hash": "value" |
| monetary | "hash": {"value": 100, "currency": "USD"} | Same |
| time | "hash": {"value": "16:15"} | {"value": "...", "timezone_id": ...} |
| address | "hash": {"value": "addr", ...} | Same + sub-fields |
| set (multi-option) | Array of integer IDs | Array of integer IDs |
Pagination
V1 used offset-based pagination. V2 uses cursor-based pagination:
# V1 — offset
GET /v1/deals?start=0&limit=100
GET /v1/deals?start=100&limit=100
# V2 — cursor
GET /api/v2/deals?limit=100
GET /api/v2/deals?limit=100&cursor=eyJpZCI6MTAwfQ
The cursor value comes from additional_data.next_cursor in the response. When next_cursor is null, you've reached the end.
// V2 Response structure
{
"success": true,
"data": [...],
"additional_data": {
"next_cursor": "eyJpZCI6MTAwfQ" // null when done
}
}
Maximum limit: 500 items per request (up from 100 in V1 for some endpoints).
Field Renames
V2 renames many standard fields across entities. Here are the most impactful ones:
Deal Fields
| V1 Field | V2 Field | Notes |
|---|---|---|
user_id | owner_id | Was nested object, now plain ID |
deleted | is_deleted | |
label | label_ids | Now array of IDs |
cc_email | smart_bcc_email | |
person_id.name | Requires separate API call | No longer embedded |
org_id.name | Requires separate API call | No longer embedded |
Person Fields
| V1 Field | V2 Field |
|---|---|
phone | phones |
email | emails |
im | ims |
active_flag | is_deleted |
Activity Fields
| V1 Field | V2 Field |
|---|---|
busy_flag | busy |
user_id | owner_id |
created_by_user_id | creator_user_id |
Flattened Object Fields
In V1, fields like user_id and org_id returned rich objects with sub-properties (.name, .email, etc.). In V2, these are plain integer IDs:
// V1 — nested object
"user_id": {
"id": 456,
"name": "John Doe",
"email": "john@company.com"
}
// V2 — flat ID
"owner_id": 456
This means if you need the user's name or email, you must make a separate GET /api/v2/users/{id} call.
Webhooks
V2 webhooks have significant payload structure changes:
Payload Structure
// V1 payload
{
"v": 1,
"current": { "id": 1, "title": "Deal", "custom_hash": "val" },
"previous": { ... },
"meta": { "action": "updated", "retry": 0 }
}
// V2 payload
{
"v": 2,
"data": { "id": 1, "title": "Deal", "custom_fields": { "hash": {...} } },
"previous": { ... },
"meta": { "action": "updated", "attempt": 0, "change_source": "app" }
}
Key Webhook Changes
| Change | V1 | V2 |
|---|---|---|
| Data wrapper | current | data |
| Retry field | meta.retry | meta.attempt |
| Custom fields | Flat on entity | Nested in custom_fields |
| Merge action | action: "merged" | action: "deleted" + meta.merged_to_id |
| Date format (products) | YYYY-MM-DD HH:mm:ss | RFC 3339 |
Removed meta fields: timestamp_micro, trans_pending, pipedrive_service_name, matches_filters, send_activity_notifications
Rate Limiting
V2 uses a token-based daily budget system instead of V1's simple requests-per-second:
- Each API call consumes tokens from a daily budget
- V2 endpoints use fewer tokens per call than V1 equivalents
- Exceeding the budget returns
429 Too Many Requests - Burst limits apply per user (requests per 2-second window)
- Budget resets every 24 hours
Use the API Usage Dashboard in Pipedrive Settings to monitor consumption and identify which endpoints to migrate first.
Search API Changes
| Feature | V1 | V2 |
|---|---|---|
| Exact matching | exact_match=true | match=exact |
| Pagination | start=N | cursor=... |
| Sorting | sort="field DESC" | sort_by=field&sort_direction=desc |
item_types | Supported | Removed |
Fields API Changes
The Fields API (/dealFields, /personFields, etc.) has important renames:
| V1 Property | V2 Property |
|---|---|
key | field_code |
name | field_name |
| Numeric ID lookup | Not supported — use hash key |
Numeric Field IDs Break in V2
GET /api/v2/dealFields/12345 returns 404. V2 requires the hash key (40-char string). Use the V1 API to resolve numeric IDs before the deprecation date.
Deprecated & Consolidated Endpoints
Many V1-specific sub-endpoints are consolidated into main endpoints with query parameters:
| V1 Endpoint | V2 Equivalent |
|---|---|
/v1/persons/{id}/deals | /api/v2/deals?person_id={id} |
/v1/organizations/{id}/deals | /api/v2/deals?org_id={id} |
/v1/pipelines/{id}/deals | /api/v2/deals?pipeline_id={id} |
/v1/stages/{id}/deals | /api/v2/deals?stage_id={id} |
/v1/deals/{id}/activities | /api/v2/activities?deal_id={id} |
Fully deprecated (no V2 replacement):
- Subscription endpoints (7 modules)
- Product variations (moved to separate API)
status=all_not_deletedparameter (V2 returns non-deleted by default)
Migration Checklist
Frequently Asked Questions
Can I use V1 and V2 simultaneously during migration?
Yes. Both APIs run in parallel until July 31, 2026. You can gradually migrate endpoint by endpoint.
Do I need to re-create webhooks?
Yes. V1 webhooks and V2 webhooks are separate. Create new V2 webhooks and delete old V1 ones once migrated.
Are OAuth refresh tokens affected?
No. OAuth tokens work with both V1 and V2. Only the endpoint URLs and request/response structure change.
What about the Leads API?
Leads remain V1-only as of March 2026. Pipedrive has not announced a timeline for V2 Leads. Continue using /v1/leads.
How do I find my custom field hash keys?
Go to Pipedrive → Settings → Data Fields. Each custom field shows its 40-character hash key. Alternatively, call GET /v1/dealFields (while V1 is still available) and look for the key property.
Automated Migration Tools
If you use Make.com or Zapier, we've built automated migration tools that handle all these changes for you:
Make.com Migration Guide
Automated V1→V2 migration for Make.com scenarios with custom field remapping and webhook preservation.
Zapier Migration Guide
Migrate Zapier Zaps in-place with the Chrome Extension — no webhook URL changes needed.
For custom integrations and direct API usage, book a free consultation — we can review your codebase and identify all V1 dependencies.
Need Help Migrating?
We've migrated thousands of Pipedrive integrations. Let us help with yours.