Bulk Operations
Create, update, or delete multiple links in a single API request for efficient batch processing.
Templates must be created before bulk creating links. Each link requires a valid templateId that references an existing template.
See Link Templates for details on creating templates.
Overview
Bulk operations allow you to process up to 100 links per request, significantly reducing API calls and improving performance for large-scale link management.
Available Operations:
- Bulk Create - Create multiple links at once
- Bulk Update - Update multiple links simultaneously
- Bulk Delete - Delete multiple links in one request
Bulk Create
Create up to 100 links in a single request.
Endpoint
POST /api/links/bulk-create
Authentication
Requires authentication via:
- Bearer token (user session)
- API key in
Authorizationheader
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
links | array | Yes | Array of link objects (max 100) |
Each link object follows the same schema as Create Link.
Response
{
"created": 25,
"links": [
{
"id": "link_1",
"shortCode": "abc123",
"originalUrl": "https://example.com/product/1",
"title": "Product 1"
},
// ... 24 more links
]
}
Example Request
curl -X POST https://api.linkforty.com/api/links/bulk-create \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"links": [
{
"templateId": "tmpl_123",
"originalUrl": "https://example.com/product/headphones",
"title": "Wireless Headphones",
"utmParameters": {
"source": "instagram",
"medium": "social",
"campaign": "spring-sale"
}
},
{
"templateId": "tmpl_123",
"originalUrl": "https://example.com/product/speakers",
"title": "Bluetooth Speakers",
"utmParameters": {
"source": "instagram",
"medium": "social",
"campaign": "spring-sale"
}
}
]
}'
Use Cases
1. Import from CSV
import { parse } from 'csv-parse/sync';
async function importLinksFromCSV(csvContent: string, templateId: string) {
const records = parse(csvContent, { columns: true });
const links = records.map(row => ({
templateId,
originalUrl: row.url,
title: row.title,
description: row.description,
utmParameters: {
source: row.utm_source,
medium: row.utm_medium,
campaign: row.utm_campaign,
},
}));
// Process in batches of 100
const batches = [];
for (let i = 0; i < links.length; i += 100) {
batches.push(links.slice(i, i + 100));
}
for (const batch of batches) {
const result = await bulkCreateLinks(batch);
console.log(`Created ${result.created} links`);
}
}
2. Generate Links for Product Catalog
async function createProductLinks(products: Product[], campaign: string) {
const links = products.map(product => ({
templateId: 'product_template',
originalUrl: `https://shop.example.com/products/${product.id}`,
title: product.name,
description: product.description,
utmParameters: {
source: 'catalog',
medium: 'website',
campaign,
content: product.category,
},
}));
return bulkCreateLinks(links);
}
Bulk Update
Update up to 100 links in a single request.
Endpoint
POST /api/links/bulk-update
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
updates | array | Yes | Array of update objects (max 100) |
Each update object contains:
| Field | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | Link ID to update |
data | object | Yes | Fields to update (same as Update Link) |
Response
{
"updated": 15,
"links": [
{
"id": "link_1",
"shortCode": "abc123",
"title": "Updated Title",
"updatedAt": "2024-03-15T10:30:00Z"
},
// ... 14 more links
]
}
Example Request
curl -X POST https://api.linkforty.com/api/links/bulk-update \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{
"id": "link_1",
"data": {
"utmParameters": {
"campaign": "summer-sale-2024"
}
}
},
{
"id": "link_2",
"data": {
"utmParameters": {
"campaign": "summer-sale-2024"
}
}
}
]
}'
Use Cases
1. Update Campaign for All Links
async function updateCampaign(linkIds: string[], newCampaign: string) {
const updates = linkIds.map(id => ({
id,
data: {
utmParameters: {
campaign: newCampaign,
},
},
}));
// Process in batches of 100
for (let i = 0; i < updates.length; i += 100) {
const batch = updates.slice(i, i + 100);
await bulkUpdateLinks(batch);
}
}
2. Extend Attribution Window
async function extendAttributionWindow(linkIds: string[], hours: number) {
const updates = linkIds.map(id => ({
id,
data: { attributionWindowHours: hours },
}));
return bulkUpdateLinks(updates);
}
3. Deactivate Multiple Links
async function deactivateLinks(linkIds: string[]) {
const updates = linkIds.map(id => ({
id,
data: { isActive: false },
}));
return bulkUpdateLinks(updates);
}
4. Update Links from Spreadsheet
async function updateFromSpreadsheet(updates: Array<{
shortCode: string;
newUrl: string;
newTitle: string;
}>) {
// First, get all links to map short codes to IDs
const allLinks = await getAllLinks();
const linkMap = new Map(allLinks.map(l => [l.shortCode, l.id]));
// Build update requests
const updateRequests = updates
.map(update => {
const linkId = linkMap.get(update.shortCode);
if (!linkId) return null;
return {
id: linkId,
data: {
originalUrl: update.newUrl,
title: update.newTitle,
},
};
})
.filter(Boolean);
return bulkUpdateLinks(updateRequests);
}
Bulk Delete
Delete up to 100 links in a single request.
Endpoint
POST /api/links/bulk-delete
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
ids | string[] | Yes | Array of link IDs (max 100) |
Response
{
"deleted": 42
}
Example Request
curl -X POST https://api.linkforty.com/api/links/bulk-delete \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"link_1",
"link_2",
"link_3"
]
}'
Use Cases
1. Delete Test Links
async function deleteTestLinks() {
const links = await getAllLinks();
const testLinkIds = links
.filter(link => link.title?.toLowerCase().includes('test'))
.map(link => link.id);
if (testLinkIds.length === 0) {
console.log('No test links found');
return;
}
// Delete in batches of 100
for (let i = 0; i < testLinkIds.length; i += 100) {
const batch = testLinkIds.slice(i, i + 100);
const result = await bulkDeleteLinks(batch);
console.log(`Deleted ${result.deleted} links`);
}
}
2. Delete Expired Links
async function deleteExpiredLinks() {
const links = await getAllLinks();
const now = new Date();
const expiredLinkIds = links
.filter(link => {
if (!link.expiresAt) return false;
return new Date(link.expiresAt) < now;
})
.map(link => link.id);
if (expiredLinkIds.length > 0) {
return bulkDeleteLinks(expiredLinkIds);
}
}
3. Delete Links with No Clicks
async function deleteUnusedLinks(minAge: number = 30) {
const links = await getAllLinks();
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - minAge);
const unusedLinkIds = links
.filter(link => {
const created = new Date(link.createdAt);
return created < cutoffDate && link.clickCount === 0;
})
.map(link => link.id);
console.log(`Found ${unusedLinkIds.length} unused links`);
if (unusedLinkIds.length > 0) {
return bulkDeleteLinks(unusedLinkIds);
}
}
TypeScript Interface
interface BulkCreateRequest {
links: CreateLinkRequest[];
}
interface BulkCreateResponse {
created: number;
links: Link[];
}
interface BulkUpdateRequest {
updates: Array<{
id: string;
data: UpdateLinkRequest;
}>;
}
interface BulkUpdateResponse {
updated: number;
links: Link[];
}
interface BulkDeleteRequest {
ids: string[];
}
interface BulkDeleteResponse {
deleted: number;
}
// Helper functions
async function bulkCreateLinks(links: CreateLinkRequest[]): Promise<BulkCreateResponse> {
const response = await fetch('https://api.linkforty.com/api/links/bulk-create', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ links }),
});
return response.json();
}
async function bulkUpdateLinks(updates: Array<{ id: string; data: UpdateLinkRequest }>): Promise<BulkUpdateResponse> {
const response = await fetch('https://api.linkforty.com/api/links/bulk-update', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ updates }),
});
return response.json();
}
async function bulkDeleteLinks(ids: string[]): Promise<BulkDeleteResponse> {
const response = await fetch('https://api.linkforty.com/api/links/bulk-delete', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids }),
});
return response.json();
}
Error Responses
Invalid Array
{
"statusCode": 400,
"error": "Bad Request",
"message": "Invalid links array"
}
Causes:
- Missing
linksfield (bulk create) - Missing
updatesfield (bulk update) - Missing
idsfield (bulk delete) - Not an array
Too Many Items
{
"statusCode": 400,
"error": "Bad Request",
"message": "Maximum 100 links allowed per bulk create"
}
Cause: More than 100 items in request.
Validation Error
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation error: Unable to generate unique short code for link at index 42"
}
Cause: One or more links in the batch failed validation.
Link Limit Exceeded
{
"statusCode": 403,
"error": "Forbidden",
"message": "Link limit exceeded for your organization"
}
Cause: Bulk create would exceed your plan's link limit.
Best Practices
1. Process in Batches of 100
async function processBatches<T>(items: T[], batchSize: number = 100) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
results.push(await processBatch(batch));
console.log(`Processed ${Math.min(i + batchSize, items.length)}/${items.length}`);
}
return results;
}
2. Handle Partial Failures
async function robustBulkCreate(links: CreateLinkRequest[]) {
const results = {
succeeded: [] as Link[],
failed: [] as { link: CreateLinkRequest; error: string }[],
};
// Process in batches
for (let i = 0; i < links.length; i += 100) {
const batch = links.slice(i, i + 100);
try {
const response = await bulkCreateLinks(batch);
results.succeeded.push(...response.links);
} catch (error) {
// If batch fails, try individually
for (const link of batch) {
try {
const created = await createLink(link);
results.succeeded.push(created);
} catch (err) {
results.failed.push({ link, error: err.message });
}
}
}
}
return results;
}
3. Validate Before Sending
function validateBulkCreate(links: CreateLinkRequest[]): string[] {
const errors = [];
if (links.length > 100) {
errors.push('Maximum 100 links per request');
}
links.forEach((link, index) => {
if (!link.originalUrl) {
errors.push(`Link ${index}: Missing originalUrl`);
}
if (!link.templateId) {
errors.push(`Link ${index}: Missing templateId`);
}
});
return errors;
}
// Usage
const errors = validateBulkCreate(links);
if (errors.length > 0) {
console.error('Validation errors:', errors);
} else {
await bulkCreateLinks(links);
}
4. Show Progress
async function bulkCreateWithProgress(links: CreateLinkRequest[]) {
const total = links.length;
let created = 0;
for (let i = 0; i < links.length; i += 100) {
const batch = links.slice(i, i + 100);
const result = await bulkCreateLinks(batch);
created += result.created;
const progress = ((created / total) * 100).toFixed(1);
console.log(`Progress: ${created}/${total} (${progress}%)`);
updateProgressBar(progress);
}
console.log('Complete!');
}
5. Retry on Failure
async function bulkCreateWithRetry(
links: CreateLinkRequest[],
maxRetries: number = 3
) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await bulkCreateLinks(links);
} catch (error) {
if (attempt === maxRetries) throw error;
console.log(`Attempt ${attempt} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
Limitations
| Limit | Value |
|---|---|
| Max items per request | 100 |
| Request size | ~1 MB |
| Rate limit | Standard rate limits apply |
Performance Tips
-
Use bulk operations instead of loops
- ✅ 100 links in 1 request = 1 API call
- ❌ 100 links in loop = 100 API calls
-
Batch your operations
- Process 100 at a time
- Show progress to user
- Handle failures gracefully
-
Validate client-side first
- Check URLs are valid
- Ensure required fields present
- Reduce failed requests
-
Use compression
- Enable gzip for large payloads
- Reduces transfer time
Related Endpoints
- Create Link - Single link creation
- Update Link - Single link update
- Delete Link - Single link deletion
- Get Link - Retrieve link details
Guides
- Creating Links - Link management basics
- Link Templates - Using templates efficiently
- UTM Parameters - Bulk campaign tracking