Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.edges.run/llms.txt

Use this file to discover all available pages before exploring further.

This guide shows how to use the scheduled run endpoint for Extract LinkedIn Connections with incremental sync via Engagement Identities, and how to migrate existing schedules to incremental mode using the /actions/linkedin-extract-connections/run/schedule route. Incremental mode fetches only new data since the last retrieval instead of the full dataset on every run. It is available only on some engagement-free actions and requires an Engagement Identity. More information here : Incremental Sync Guide

Prerequisites

  • Engagement Identities enabled in your workspace and at least one Engagement Identity created with a connected LinkedIn account
  • API key with access to runs, schedules, and identities

Shared setup (reuse in your code)

The examples below use a single base URL and a small request helper so headers and error handling stay consistent. Define these once and reuse them across Part 1 and Part 2.
const EDGES_API_BASE = 'https://api.edges.run/v1';

async function edgesRequest(apiKey, url, { method = 'GET', body } = {}) {
  const res = await fetch(url, {
    method,
    headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
    ...(body != null && { body: JSON.stringify(body) }),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.message ?? `Request failed: ${res.status}`);
  }
  return res.json();
}

Part 1: Implementing Incremental Mode

1.1 Create a new scheduled run in incremental mode

To schedule Extract LinkedIn Connections in incremental mode, send sync_mode: "incremental" in the parameters object and use an Engagement Identity in identity_ids. Behavior:
  • First run: Full sync (capped by max_results).
  • Next runs: Only new connections since the last run. No need to call continue; each scheduled execution is automatically incremental.
In schedule mode, if the previous run is still in progress when the next iteration is due, that iteration is skipped. The following one will run the incremental update.
JavaScript example: create a scheduled incremental Extract Connections run
// Uses EDGES_API_BASE and edgesRequest from the shared setup above.

async function createIncrementalExtractConnectionsSchedule(apiKey, options) {
  const {
    identityId,
    linkedinUrl,
    callbackUrl,
    cron = '0 9 * * *',      // daily at 9am
    timezone = 'Europe/Paris',
    maxResults,
  } = options;

  return edgesRequest(apiKey, `${EDGES_API_BASE}/actions/linkedin-extract-connections/run/schedule`, {
    method: 'POST',
    body: {
      inputs: [{ linkedin_url: linkedinUrl }],
      callback: { url: callbackUrl },
      identity_ids: [identityId],
      cron,
      timezone,
      parameters: {
        sync_mode: 'incremental',
        ...(maxResults != null && { max_results: maxResults }),
      },
    },
  });
}

// Usage: ensure identityId is an Engagement Identity (type === 'engagement')
// const schedule = await createIncrementalExtractConnectionsSchedule(process.env.EDGES_API_KEY, {
//   identityId: 'id_xxx',
//   linkedinUrl: 'https://linkedin.com/in/johndoe',
//   callbackUrl: 'https://yourdomain.com/webhooks/edges',
// });

1.2 Async mode: continue an incremental run

If you use async instead of schedule, you trigger each incremental update by calling the continue endpoint after the previous run has finished. JavaScript example: run async and continue later (Extract Connections)
// Uses EDGES_API_BASE and edgesRequest from the shared setup above.

async function runExtractConnectionsIncrementalAsync(apiKey, options) {
  return edgesRequest(apiKey, `${EDGES_API_BASE}/actions/linkedin-extract-connections/run/async`, {
    method: 'POST',
    body: {
      inputs: [{ linkedin_url: options.linkedinUrl }],
      callback: { url: options.callbackUrl },
      identity_ids: [options.identityId],
      parameters: {
        sync_mode: 'incremental',
        ...(options.maxResults != null && { max_results: options.maxResults }),
      },
    },
  });
}

async function continueIncrementalRun(apiKey, runUid) {
  return edgesRequest(apiKey, `${EDGES_API_BASE}/runs/${runUid}/continue`, { method: 'POST' });
}

Part 2: Migrating full-mode schedules to incremental

Schedules cannot be updated in place (e.g. to add parameters.sync_mode). To switch existing Extract Connections schedules from full to incremental, you must:
  1. List schedules for linkedin-extract-connections (optionally restricted to Engagement Identities).
  2. For each schedule: get full details, cancel it, then create a new schedule with the same config plus parameters.sync_mode: "incremental".
Use only Engagement Identities for the new schedules; incremental sync is not supported for standard identities. Schedule objects in the examples have optional fields such as scheduled_run_uid, uid, identity_ids, inputs, callback, cron, timezone, parameters, max_results.

2.1 Get Engagement Identity IDs

Filter identities with type: "engagement" so you only migrate schedules that use them (or only recreate schedules using those identities). The helper below paginates so all identities are returned (not only the first 100).
// Uses EDGES_API_BASE and edgesRequest from the shared setup above.

async function getEngagementIdentityIds(apiKey) {
  const ids = [];
  let offset = 0;
  const limit = 100;
  let hasMore = true;
  while (hasMore) {
    const data = await edgesRequest(
      apiKey,
      `${EDGES_API_BASE}/identities?limit=${limit}&offset=${offset}`
    );
    const items = data.data ?? data.items ?? (Array.isArray(data) ? data : []);
    ids.push(...items.filter((id) => id.type === 'engagement').map((id) => id.uid));
    hasMore = items.length === limit;
    offset += limit;
  }
  return ids;
}

2.2 List Extract Connections schedules (full mode)

List schedules for the Extract Connections action. The helper paginates so all matching schedules are returned.
// Uses EDGES_API_BASE and edgesRequest from the shared setup above.

async function listExtractConnectionsSchedules(apiKey) {
  const schedules = [];
  let offset = 0;
  const limit = 100;
  let hasMore = true;
  while (hasMore) {
    const params = new URLSearchParams({
      action_name: 'linkedin-extract-connections',
      status: 'ACTIVE',
      limit: String(limit),
      offset: String(offset),
    });
    const data = await edgesRequest(apiKey, `${EDGES_API_BASE}/schedules?${params}`);
    const items = data.data ?? data.items ?? (Array.isArray(data) ? data : []);
    schedules.push(...items);
    hasMore = items.length === limit;
    offset += limit;
  }
  return schedules;
}

2.3 Filter by Engagement Identity (optional)

If the list response includes identity_ids (or similar), keep only schedules whose identities are in your set of Engagement Identity UIDs. If the API does not return identity info, you can still migrate all Extract Connections schedules and rely on the fact that the new schedule you create will use an Engagement Identity.
function filterSchedulesByEngagementIdentities(schedules, engagementIdentityIds) {
  const set = new Set(engagementIdentityIds);
  return schedules.filter((s) => {
    const ids = s.identity_ids ?? [];
    return ids.some((id) => set.has(id));
  });
}

2.4 Cancel and recreate each schedule with incremental mode

For each schedule to migrate:
  1. GET the schedule by scheduled_run_uid to get inputs, callback, cron, timezone, identity_ids, etc.
  2. POST .../schedules/{scheduled_run_uid}/cancel to delete it.
  3. POST .../actions/linkedin-extract-connections/run/schedule with the same config plus parameters: { sync_mode: 'incremental' }.
Migration helpers (use edgesRequest from the shared setup):
async function getSchedule(apiKey, scheduledRunUid) {
  return edgesRequest(apiKey, `${EDGES_API_BASE}/schedules/${scheduledRunUid}`);
}

async function cancelSchedule(apiKey, scheduledRunUid) {
  return edgesRequest(apiKey, `${EDGES_API_BASE}/schedules/${scheduledRunUid}/cancel`, {
    method: 'POST',
  });
}

async function migrateScheduleToIncremental(apiKey, scheduledRunUid) {
  const schedule = await getSchedule(apiKey, scheduledRunUid);
  await cancelSchedule(apiKey, scheduledRunUid);

  const body = {
    inputs: schedule.inputs ?? [],
    callback: schedule.callback ?? {},
    identity_ids: schedule.identity_ids ?? [],
    ...(schedule.cron && { cron: schedule.cron }),
    ...(schedule.timezone && { timezone: schedule.timezone }),
    ...(schedule.schedule_at && { schedule_at: schedule.schedule_at }),
    parameters: {
      // Add parameters if needed manually, because you can't get back parameters from scheduled runs
      sync_mode: 'incremental',
    },
    // Add max_results if needed manually
  };

  return edgesRequest(apiKey, `${EDGES_API_BASE}/actions/linkedin-extract-connections/run/schedule`, {
    method: 'POST',
    body,
  });
}
If you don’t need to get older results with the first incremental mode. Keep the max_results low to avoid time consumption

2.5 Full migration script (Extract Connections + Engagement Identities)

End-to-end example: list active Extract Connections schedules, restrict to those using Engagement Identities, then migrate each to incremental by cancel + recreate. Runs sequentially to avoid rate limits.
// Uses EDGES_API_BASE, edgesRequest, and all helpers above.

async function migrateExtractConnectionsSchedulesToIncremental(apiKey) {
  const engagementIds = await getEngagementIdentityIds(apiKey);
  if (engagementIds.length === 0) {
    console.warn('No Engagement Identities found. Incremental mode requires them.');
    return [];
  }

  const schedules = await listExtractConnectionsSchedules(apiKey);
  const toMigrate = filterSchedulesByEngagementIdentities(schedules, engagementIds);

  const results = [];
  for (const s of toMigrate) {
    const uid = s.scheduled_run_uid ?? s.uid;
    if (!uid) continue;
    try {
      const newSchedule = await migrateScheduleToIncremental(apiKey, uid);
      results.push({ uid, newSchedule });
      console.log(`Migrated schedule ${uid}`);
    } catch (e) {
      const message = e instanceof Error ? e.message : String(e);
      results.push({ uid, error: message });
      console.error(`Failed to migrate ${uid}:`, message);
    }
  }
  return results;
}

// Run migration
migrateExtractConnectionsSchedulesToIncremental(process.env.EDGES_API_KEY)
  .then((results) => console.log('Migration results:', results))
  .catch((err) => console.error('Migration failed:', err));

Summary

GoalApproach
New incremental schedulePOST /v1/actions/linkedin-extract-connections/run/schedule with parameters.sync_mode: "incremental" and an Engagement Identity in identity_ids.
Async incrementalSame parameters on async run; then call POST /v1/runs/{run_uid}/continue for each subsequent fetch.
Existing full-mode schedules → incrementalList schedules (GET /v1/schedules?action_name=linkedin-extract-connections), filter by Engagement Identity if needed, then for each: GET schedule → cancel → recreate with sync_mode: "incremental".
For more on incremental sync behavior and limits, see the Incremental Sync guide and Continue a run.