CRM Data Export
📋 Overview
Pipedrive CRM data export enables retrieving deals, contacts (persons), organizations, notes, pipelines, and stages with cursor-based pagination. All endpoints automatically refresh access tokens before making API requests.
Supported Export Types:
persons- Contact recordsorganizations- Company recordsdeals- Sales opportunitiesnotes- Notes attached to recordspipelines- Sales pipeline definitionsstages- Pipeline stages
🔌 Export Endpoint
Endpoint: GET /v1/e/pipedrive/export/:type
Purpose: Export CRM data with automatic pagination support
Headers:
Authorization: Bearer {jwt_token}
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Export type: persons, organizations, deals, notes, pipelines, stages |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | No | 100 | Number of records per page (max varies by type) |
start | number | No | 0 | Cursor position for pagination (use next_start from previous response) |
Authentication: Requires JWT token with valid account context
How It Works
- Token Refresh:
getTokenmiddleware retrieves and refreshes access token - API Domain: Middleware provides account-specific
api_domain - Validate Type: Controller checks if
typeis in allowed list - Fetch Data: Call Pipedrive API with dynamic domain
- Return Response: Include data with pagination metadata
Example Request:
curl -X GET \
'https://api.dashclicks.com/v1/e/pipedrive/export/deals?limit=100&start=0' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
📊 Export Types
1. Persons (Contacts)
Type: persons
Pipedrive API: GET /v1/persons
Description: Individual contact records
Request:
GET /v1/e/pipedrive/export/persons?limit=100&start=0
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 12345,
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"email": [
{
"value": "john.doe@example.com",
"primary": true,
"label": "work"
}
],
"phone": [
{
"value": "+1234567890",
"primary": true,
"label": "work"
}
],
"org_id": {
"name": "Example Corp",
"value": 67890
},
"owner_id": {
"id": 111,
"name": "Sales Rep",
"email": "sales@dashclicks.com"
},
"add_time": "2024-01-15 10:30:00",
"update_time": "2024-03-20 14:45:00",
"visible_to": "3",
"label": 2,
"cc_email": "person-12345@pipedrivemail.com"
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": true,
"next_start": 100
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique person ID |
name | string | Full name |
first_name | string | First name |
last_name | string | Last name |
email | array | Email addresses with labels |
phone | array | Phone numbers with labels |
org_id | object | Associated organization |
owner_id | object | Person owner (sales rep) |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
visible_to | string | Visibility setting (1=owner, 3=entire company) |
label | number | Label ID |
cc_email | string | BCC email for this person |
2. Organizations (Companies)
Type: organizations
Pipedrive API: GET /v1/organizations
Description: Company/organization records
Request:
GET /v1/e/pipedrive/export/organizations?limit=100&start=0
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 67890,
"name": "Example Corp",
"address": "123 Business St, City, State 12345",
"address_country": "United States",
"owner_id": {
"id": 111,
"name": "Sales Manager",
"email": "manager@dashclicks.com"
},
"people_count": 15,
"open_deals_count": 3,
"closed_deals_count": 12,
"won_deals_count": 10,
"lost_deals_count": 2,
"add_time": "2023-06-10 09:00:00",
"update_time": "2024-03-20 11:30:00",
"visible_to": "3",
"label": 1,
"cc_email": "org-67890@pipedrivemail.com"
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": true,
"next_start": 100
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique organization ID |
name | string | Company name |
address | string | Full address |
address_country | string | Country name |
owner_id | object | Organization owner |
people_count | number | Number of associated contacts |
open_deals_count | number | Number of open deals |
closed_deals_count | number | Number of closed deals |
won_deals_count | number | Number of won deals |
lost_deals_count | number | Number of lost deals |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
visible_to | string | Visibility setting |
label | number | Label ID |
cc_email | string | BCC email for this organization |
3. Deals (Opportunities)
Type: deals
Pipedrive API: GET /v1/deals
Description: Sales opportunities
Request:
GET /v1/e/pipedrive/export/deals?limit=100&start=0
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 54321,
"title": "Enterprise Package - Q1 2024",
"value": 50000,
"currency": "USD",
"status": "open",
"stage_id": 5,
"stage_order_nr": 3,
"pipeline_id": 1,
"person_id": {
"name": "John Doe",
"value": 12345
},
"org_id": {
"name": "Example Corp",
"value": 67890
},
"owner_id": {
"id": 111,
"name": "Sales Rep",
"email": "sales@dashclicks.com"
},
"expected_close_date": "2024-03-31",
"add_time": "2024-01-10 14:00:00",
"update_time": "2024-03-15 16:20:00",
"close_time": null,
"won_time": null,
"lost_time": null,
"visible_to": "3",
"probability": 75,
"lost_reason": null
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": true,
"next_start": 100
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique deal ID |
title | string | Deal name/title |
value | number | Deal monetary value |
currency | string | Currency code (USD, EUR, etc.) |
status | string | Deal status: open, won, lost, deleted |
stage_id | number | Current pipeline stage ID |
stage_order_nr | number | Stage order in pipeline |
pipeline_id | number | Associated pipeline ID |
person_id | object | Associated contact |
org_id | object | Associated organization |
owner_id | object | Deal owner |
expected_close_date | string | Expected close date (YYYY-MM-DD) |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
close_time | string | When deal was closed (won/lost) |
won_time | string | When deal was won |
lost_time | string | When deal was lost |
visible_to | string | Visibility setting |
probability | number | Win probability percentage |
lost_reason | string | Reason for loss (if lost) |
4. Notes
Type: notes
Pipedrive API: GET /v1/notes
Description: Notes attached to deals, persons, or organizations
Request:
GET /v1/e/pipedrive/export/notes?limit=100&start=0
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 98765,
"content": "Initial discovery call completed. Customer interested in enterprise plan.",
"add_time": "2024-03-18 10:15:00",
"update_time": "2024-03-18 10:15:00",
"user": {
"id": 111,
"name": "Sales Rep",
"email": "sales@dashclicks.com"
},
"deal": {
"title": "Enterprise Package - Q1 2024",
"id": 54321
},
"person": {
"name": "John Doe",
"id": 12345
},
"organization": {
"name": "Example Corp",
"id": 67890
},
"pinned_to_deal_flag": false,
"pinned_to_person_flag": false,
"pinned_to_organization_flag": false
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": true,
"next_start": 100
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique note ID |
content | string | Note text content |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
user | object | Note author |
deal | object | Associated deal (if any) |
person | object | Associated person (if any) |
organization | object | Associated organization (if any) |
pinned_to_deal_flag | boolean | Pinned to deal |
pinned_to_person_flag | boolean | Pinned to person |
pinned_to_organization_flag | boolean | Pinned to organization |
5. Pipelines
Type: pipelines
Pipedrive API: GET /v1/pipelines
Description: Sales pipeline definitions
Request:
GET /v1/e/pipedrive/export/pipelines
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 1,
"name": "Sales Pipeline",
"url_title": "sales-pipeline",
"order_nr": 1,
"active": true,
"deal_probability": true,
"add_time": "2023-01-10 08:00:00",
"update_time": "2024-02-15 12:00:00",
"selected": true
},
{
"id": 2,
"name": "Partner Pipeline",
"url_title": "partner-pipeline",
"order_nr": 2,
"active": true,
"deal_probability": false,
"add_time": "2023-03-20 09:30:00",
"update_time": "2023-12-01 14:00:00",
"selected": false
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": false,
"next_start": null
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique pipeline ID |
name | string | Pipeline name |
url_title | string | URL-friendly pipeline name |
order_nr | number | Display order |
active | boolean | Whether pipeline is active |
deal_probability | boolean | Whether deals use probability |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
selected | boolean | Currently selected pipeline |
6. Stages
Type: stages
Pipedrive API: GET /v1/stages
Description: Pipeline stages (steps in sales process)
Request:
GET /v1/e/pipedrive/export/stages
Response Structure:
{
"success": true,
"message": "SUCCESS",
"data": [
{
"id": 1,
"order_nr": 1,
"name": "Lead In",
"active_flag": true,
"deal_probability": 10,
"pipeline_id": 1,
"rotten_flag": false,
"rotten_days": null,
"add_time": "2023-01-10 08:00:00",
"update_time": "2023-06-15 10:00:00",
"pipeline_name": "Sales Pipeline"
},
{
"id": 2,
"order_nr": 2,
"name": "Qualification",
"active_flag": true,
"deal_probability": 25,
"pipeline_id": 1,
"rotten_flag": true,
"rotten_days": 14,
"add_time": "2023-01-10 08:00:00",
"update_time": "2023-08-20 11:30:00",
"pipeline_name": "Sales Pipeline"
}
],
"pagination": {
"start": 0,
"limit": 100,
"more_items_in_collection": false,
"next_start": null
}
}
Key Fields:
| Field | Type | Description |
|---|---|---|
id | number | Unique stage ID |
order_nr | number | Stage order in pipeline |
name | string | Stage name |
active_flag | boolean | Whether stage is active |
deal_probability | number | Default win probability for this stage |
pipeline_id | number | Parent pipeline ID |
rotten_flag | boolean | Whether deals can become "rotten" |
rotten_days | number | Days until deal becomes rotten (if flag enabled) |
add_time | string | Created timestamp |
update_time | string | Last updated timestamp |
pipeline_name | string | Parent pipeline name |
🔄 Pagination
Pipedrive uses cursor-based pagination with start and next_start parameters.
How Pagination Works
- First Request: Omit
startparameter (defaults to 0) - Check
more_items_in_collection: Iftrue, more pages exist - Use
next_start: For next request, usestart={next_start}value - Repeat: Continue until
more_items_in_collectionisfalse
Pagination Response Structure
{
"pagination": {
"start": 0, // Current cursor position
"limit": 100, // Records per page
"more_items_in_collection": true, // More pages available?
"next_start": 100 // Use this value for next request
}
}
Example: Paginate Through All Deals
async function getAllDeals(jwtToken) {
let allDeals = [];
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
// Build URL with start parameter
const url = `/v1/e/pipedrive/export/deals?limit=100${start ? `&start=${start}` : ''}`;
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${jwtToken}` },
});
// Append data
allDeals = [...allDeals, ...response.data.data];
// Check for more pages
const pagination = response.data.pagination;
if (pagination.more_items_in_collection) {
start = pagination.next_start;
} else {
hasMoreItems = false;
}
}
return allDeals;
}
Best Practices
- Use appropriate limit: 100-200 is recommended (check Pipedrive limits)
- Check
more_items_in_collection: Don't assumenext_start !== null - Handle rate limits: Pipedrive may throttle requests
- Store cursor position: For resumable imports, save
next_startvalue - Avoid large limits: Smaller pages = faster response times
🎯 Use Cases
1. Export All CRM Data
// Export all supported data types
async function exportAllPipedriveData(jwtToken) {
const types = ['persons', 'organizations', 'deals', 'notes', 'pipelines', 'stages'];
const exportedData = {};
for (const type of types) {
exportedData[type] = [];
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/${type}?limit=200${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
exportedData[type] = [...exportedData[type], ...response.data.data];
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
console.log(`Exported ${exportedData[type].length} ${type}`);
}
return exportedData;
}
2. Sync Deals to Database
// Sync Pipedrive deals to local database
async function syncDealsToDatabase(accountId, jwtToken) {
let start = 0;
let hasMoreItems = true;
let totalSynced = 0;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/deals?limit=100${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
const deals = response.data.data;
// Upsert deals to database
for (const deal of deals) {
await DealModel.findOneAndUpdate(
{
accountId: accountId,
pipedriveId: deal.id,
},
{
accountId: accountId,
pipedriveId: deal.id,
title: deal.title,
value: deal.value,
currency: deal.currency,
status: deal.status,
stageId: deal.stage_id,
pipelineId: deal.pipeline_id,
personId: deal.person_id?.value,
orgId: deal.org_id?.value,
expectedCloseDate: deal.expected_close_date,
lastSynced: new Date(),
rawData: deal,
},
{ upsert: true, new: true },
);
}
totalSynced += deals.length;
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
console.log(`Synced ${totalSynced} deals`);
return totalSynced;
}
3. Build Pipeline Visualization
// Get pipeline structure with stages and deal counts
async function getPipelineVisualization(jwtToken) {
// 1. Get all pipelines
const pipelinesResponse = await axios.get('/v1/e/pipedrive/export/pipelines', {
headers: { Authorization: `Bearer ${jwtToken}` },
});
const pipelines = pipelinesResponse.data.data;
// 2. Get all stages
const stagesResponse = await axios.get('/v1/e/pipedrive/export/stages', {
headers: { Authorization: `Bearer ${jwtToken}` },
});
const stages = stagesResponse.data.data;
// 3. Get all deals
const deals = await getAllDeals(jwtToken);
// 4. Build visualization data
const visualization = pipelines.map(pipeline => ({
id: pipeline.id,
name: pipeline.name,
stages: stages
.filter(stage => stage.pipeline_id === pipeline.id)
.sort((a, b) => a.order_nr - b.order_nr)
.map(stage => ({
id: stage.id,
name: stage.name,
order: stage.order_nr,
probability: stage.deal_probability,
dealCount: deals.filter(deal => deal.stage_id === stage.id).length,
totalValue: deals
.filter(deal => deal.stage_id === stage.id)
.reduce((sum, deal) => sum + deal.value, 0),
})),
}));
return visualization;
}
// Helper function from previous example
async function getAllDeals(jwtToken) {
let allDeals = [];
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/deals?limit=100${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
allDeals = [...allDeals, ...response.data.data];
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
return allDeals;
}
4. Export Contacts with Organizations
// Export persons with their associated organization details
async function exportContactsWithOrganizations(jwtToken) {
// 1. Get all organizations
let organizations = [];
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/organizations?limit=100${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
organizations = [...organizations, ...response.data.data];
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
// Create organization lookup map
const orgMap = new Map();
organizations.forEach(org => orgMap.set(org.id, org));
// 2. Get all persons
let persons = [];
start = 0;
hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/persons?limit=100${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
persons = [...persons, ...response.data.data];
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
// 3. Enrich persons with full organization data
const enrichedContacts = persons.map(person => ({
id: person.id,
name: person.name,
email: person.email?.[0]?.value,
phone: person.phone?.[0]?.value,
organization: person.org_id?.value ? orgMap.get(person.org_id.value) : null,
}));
return enrichedContacts;
}
5. Generate Sales Report
// Generate sales report with pipeline metrics
async function generateSalesReport(jwtToken, startDate, endDate) {
// Get all deals
let deals = [];
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/deals?limit=200${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
deals = [...deals, ...response.data.data];
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
// Filter deals by date range
const filteredDeals = deals.filter(deal => {
const wonTime = deal.won_time ? new Date(deal.won_time) : null;
return wonTime && wonTime >= startDate && wonTime <= endDate;
});
// Calculate metrics
const report = {
totalDeals: filteredDeals.length,
totalValue: filteredDeals.reduce((sum, deal) => sum + deal.value, 0),
averageDealValue:
filteredDeals.length > 0
? filteredDeals.reduce((sum, deal) => sum + deal.value, 0) / filteredDeals.length
: 0,
wonDeals: filteredDeals.filter(d => d.status === 'won').length,
lostDeals: filteredDeals.filter(d => d.status === 'lost').length,
openDeals: filteredDeals.filter(d => d.status === 'open').length,
byPipeline: {},
};
// Group by pipeline
filteredDeals.forEach(deal => {
const pipelineId = deal.pipeline_id;
if (!report.byPipeline[pipelineId]) {
report.byPipeline[pipelineId] = {
count: 0,
totalValue: 0,
wonCount: 0,
lostCount: 0,
};
}
report.byPipeline[pipelineId].count++;
report.byPipeline[pipelineId].totalValue += deal.value;
if (deal.status === 'won') report.byPipeline[pipelineId].wonCount++;
if (deal.status === 'lost') report.byPipeline[pipelineId].lostCount++;
});
return report;
}
⚠️ Error Handling
1. Invalid Export Type
Request:
GET /v1/e/pipedrive/export/invalid_type
Response: 400 Bad Request
{
"success": false,
"message": "Invalid type. Allowed types: persons, organizations, deals, notes, pipelines, stages",
"error": "INVALID_TYPE"
}
2. Token Not Found
Scenario: User hasn't connected Pipedrive
Response: 401 Unauthorized
{
"success": false,
"message": "User oauth token not found. Please redirect to login",
"error": "TOKEN_NOT_FOUND"
}
3. Token Invalidated
Scenario: User revoked Pipedrive access
Response: 401 Unauthorized
{
"success": false,
"message": "TOKEN_INVALIDATED",
"error": "TOKEN_INVALIDATED"
}
4. Pipedrive API Error
Scenario: Pipedrive API returns error
Response: 502 Bad Gateway
{
"success": false,
"message": "Pipedrive API error: {error_details}",
"error": "EXTERNAL_API_ERROR"
}
5. Rate Limit Exceeded
Scenario: Too many requests to Pipedrive
Response: 429 Too Many Requests
{
"success": false,
"message": "Rate limit exceeded. Please try again later.",
"error": "RATE_LIMIT_EXCEEDED"
}
📊 Performance Optimization
1. Batch Processing
Process records in batches to avoid memory issues:
async function processBatches(jwtToken, type, batchSize = 100) {
let start = 0;
let hasMoreItems = true;
while (hasMoreItems) {
const response = await axios.get(
`/v1/e/pipedrive/export/${type}?limit=${batchSize}${start ? `&start=${start}` : ''}`,
{ headers: { Authorization: `Bearer ${jwtToken}` } },
);
// Process batch immediately
await processBatch(response.data.data);
if (response.data.pagination.more_items_in_collection) {
start = response.data.pagination.next_start;
} else {
hasMoreItems = false;
}
}
}
2. Parallel Type Fetching
Fetch multiple types in parallel:
async function fetchMultipleTypes(jwtToken, types) {
const promises = types.map(type => getAllDataForType(jwtToken, type));
const results = await Promise.all(promises);
return types.reduce((acc, type, index) => {
acc[type] = results[index];
return acc;
}, {});
}
3. Incremental Sync
Use update_time to fetch only changed records:
// Store last sync time
const lastSyncTime = '2024-03-15 10:00:00';
// Filter deals updated after last sync
const recentDeals = allDeals.filter(deal => new Date(deal.update_time) > new Date(lastSyncTime));