# Instagram JSON Export Cleaner and Deduplicator (`cygnix_labs/cygnix-instagram-export-cleaner`) Actor

Normalize supplied Instagram post JSON, remove duplicate selections and keep only unseen records using your checkpoint. Preserve carousel children and separate views from plays. Get clean rows and per-row receipts. $0.05 per completed cleanup report. No scraping.

- **URL**: https://apify.com/cygnix_labs/cygnix-instagram-export-cleaner.md
- **Developed by:** [Umair Saleem Mohammad Saleem](https://apify.com/cygnix_labs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$50.00 / 1,000 completed cleanup reports

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Instagram JSON Export Cleaner and Deduplicator

Turn supported Instagram post JSON into normalized parent records, a new-only selection and a receipt explaining what happened to every input row. Use the output to prepare your own spreadsheet, reporting or automation workflow.

You provide the post data. The cleaner preserves supplied carousel children, keeps unknown counts separate from zero, and keeps video views separate from plays. It does not fetch Instagram, log in, call another scraper Actor, use proxies, download media, or establish rights to content. You supply exports you are authorized to process.

USD 0.05 per verified completed bounded cleanup report, including a valid zero-new report. There is no startup or per-post developer charge. Platform usage during the run is included in the configured completion price; post-run dataset interactions may incur standard Apify platform costs. See [Pricing and completion policy](#pricing-and-completion-policy).

### Try the exact fictional example

The unchanged fictional demo is fee-eligible at USD 0.05 per completed report. Leaving its rights checkbox off is a narrow input exception, not a free-run offer.

1. Open this Actor's Input tab, switch to JSON and paste the [complete exact fictional JSON below](#exact-fictional-input-json). Keep every field and value unchanged, including timezone, epochUnit and rightsAcknowledged=false; do not add a checkpoint or optional fields. Keep observedAt exactly as 2026-10-13T12:00:00Z: adding .000 before Z changes the string and leaves the narrow exception. If starting from the [existing public example](https://apify.com/cygnix_labs/cygnix-instagram-export-cleaner/examples/cygnix-instagram-export-cleaner-task), replace its input with this exact JSON before running. Stay in JSON view and check the complete input before starting.
2. Check the run's pricing panel. Use 512 MiB memory, a 120-second timeout and a maximum cost of USD 0.05, the configuration used for the historical default-example check. The completion price requires at least USD 0.05 of available run budget.
3. Start the run once. The unchanged first example selects three parent records. A carousel is one parent record; its supplied children remain inside it.
4. Before treating delivery as complete, require both a SUCCEEDED Actor run and OUTPUT.delivery_status equal to complete. Open REPORT to inspect counts, selected records and per-row receipts; inspect CHECKPOINT and validate the manifest/checkpoint digests as described in [Caller-owned checkpoint workflow](#caller-owned-checkpoint-workflow).
5. Save the full REPORT and CHECKPOINT privately. Do not publish real inputs, checkpoints or storage download links. If the run fails or delivery/billing is unclear, follow [Billing journal, deadlines and recovery](#billing-journal-deadlines-and-recovery) before retrying or advancing a checkpoint.

For your own data, map the export to the [supported fields](#supported-fields), choose a stable namespace, provide a fresh batchId and an offset-aware observedAt, and affirm rightsAcknowledged only after confirming your authorization. If the editor changes any field, including timestamp formatting, the input no longer qualifies for the false-acknowledgment exception. Restore the exact JSON, or use true only after confirming your authorization for that supplied data. Arbitrary vendor JSON and Instagram account-archive ZIP files are not automatically converted.

#### What the fictional result looks like

This is an excerpt of the historical synthetic REPORT, showing selected fields and only one of the three selected records. It is not the complete report or API schema.

```json
{
  "input_records": 3,
  "counts": {
    "selected": 3,
    "already_selected": 0,
    "duplicate_in_input": 0,
    "filtered_out": 0,
    "error": 0
  },
  "records": [
    {
      "record_id": "instagram:post:4444444444444444444",
      "kind": "reel",
      "metrics": {
        "likes": 0,
        "comments": null,
        "views": null,
        "plays": 72
      }
    }
  ]
}
```

Here, zero likes remain zero, unavailable views remain null, and 72 plays stay separate from views. The complete report also shows the carousel's supplied children and receipts naming omitted fields such as customField and sourceExtra. No referenced URL is fetched.

### What you get

- Stable parent IDs with ID/shortcode aliases and visible identity conflicts
- One record per carousel, with allowed supplied child fields and their order retained
- Captions, owner fields, post URLs, and media URL references preserved when supplied; URLs are never fetched
- Unknown engagement counts kept null, zero kept zero, and plays kept separate from views
- Explicit inclusive/exclusive date filters that check every input, including old pinned rows
- A per-row receipt showing selection, prior selection, duplicates, filtering, errors, and omitted field names
- A namespace-bound checkpoint that you store and send to the next cleanup

Comments and arbitrary extra fields are outside the allowed-field contract. They are omitted with named receipts. No content is invented or refreshed.

### Frequently asked questions

#### Does this scrape profiles, reels or hashtags?

No. It processes supplied post objects. Obtain authorized data separately and map it to the supported Image, Video or Sidecar format. Instagram download ZIP files and arbitrary vendor JSON need mapping first.

#### What happens to carousel children?

The parent remains one selected record. Supplied allowed child fields and their order are retained; children are not independently selected parent rows.

#### Why do I see null instead of zero? Will it refresh engagement?

Missing or unavailable metrics remain unknown. Known zero values stay zero, and views and plays remain separate. The cleaner does not acquire new data. Already-selected identities do not become new because their metrics, captions or children changed; this is not an update/change feed.

#### What does the USD 0.05 fee cover?

One eligible completed bounded cleanup report, including the unchanged fictional demo, a valid zero-new result, a valid date-filtered result or a report containing both valid rows and row errors. It is not a per-post fee. In-run platform usage is included; post-run storage interactions can cost extra. Check the current run pricing panel and the [complete billing policy](#pricing-and-completion-policy) before starting.

#### How large can my input be?

The hard input-row ceiling is 5,000, with combined input, report, dataset, checkpoint, per-row and child limits. Start with a smaller representative batch and read [Bounds and stopping behavior](#bounds-and-stopping-behavior). A 5,000-row ceiling does not guarantee every 5,000-row export will fit.

#### Can I run the same namespace in parallel?

Serialize the entire checkpoint-read, processing, durable delivery and checkpoint-adoption cycle. There is no server-side shared lock or exactly-once delivery guarantee. See the [checkpoint workflow](#caller-owned-checkpoint-workflow) before automating.

#### Can I use it with n8n, Make or Google Sheets?

Your own workflow can consume the outputs. Mapping, checkpoint storage, concurrency and downstream idempotency remain your workflow's responsibility. No end-to-end certification for those products is claimed.

#### What should I do if delivery or billing is unclear?

Preserve the run artifacts and inspect the complete manifest, digests and authoritative platform charge record before retrying. A failure after delivery or charging may still carry a fee. Use the [documented recovery](#billing-journal-deadlines-and-recovery) and [support/charge-review process](#support-and-charge-review); no automatic refund or free retry is promised.

### Input

Start with the exact hosted prefill above. For your own data, use the following input contract and affirm your rights only after confirming your authorization.

Required:

- namespace: stable campaign/export stream name, e.g. brand-monitoring
- batchId: unique name for this cleanup batch, e.g. 2026-10-13-import-01
- observedAt: offset-aware ISO timestamp, e.g. 2026-10-13T13:00:00Z
- posts: array of supplied Image, Video, or Sidecar objects
- rightsAcknowledged: true when you are authorized to use your supplied exports; false is accepted only for the exact unchanged fictional prefill

Optional: the previous checkpoint, since, until, timezone, assumeNaiveTimezone, and epochUnit. Use the [exact fictional Input JSON](#exact-fictional-input-json) for your first hosted example. Supply the documented post-object format; arbitrary vendor exports or Instagram account-archive ZIP files are not automatically converted. Map other formats to the supported fields first.

Pass the same batchId, observedAt, posts, options, and original prior checkpoint for an exact retry. Use a new batchId for the next cleanup. Reusing the immediately preceding batchId with its already-updated checkpoint is rejected. Missing checkpoint deliberately starts fresh history and can reselect previously delivered posts.

### Keep only records you have not selected before

Pass the previous successful checkpoint with your next batch in the same namespace. New means not previously selected in that checkpoint: an old unseen post can be selected, while changed likes, captions or children on an already-selected identity do not make it new.

Use a fresh batchId and an observation time no earlier than the checkpoint's last run. Serialize the entire read-process-deliver-adopt cycle below. Missing the checkpoint deliberately starts fresh history and can select old records again. A valid zero-new result still costs USD 0.05 per completed report.

### Caller-owned checkpoint workflow

Serialize the entire read-current-checkpoint → process → deliver/durably save report → adopt-next-checkpoint cycle for each namespace. Locking only the final checkpoint write is insufficient: two runs can start from the same parent and produce overlapping output or lose one another's identity history. This package has no server-side shared state or cloud lock.

1. Read your current checkpoint and hold the namespace's workflow lock/concurrency slot.
2. Run the cleanup with a fresh batchId and stable observedAt.
3. Require a SUCCEEDED Actor run and OUTPUT.delivery_status equal to complete. Validate OUTPUT.input_checkpoint_digest against the checkpoint you supplied, and match OUTPUT.next_checkpoint_digest with REPORT and CHECKPOINT.
4. Durably save the full REPORT before advancing the checkpoint. Handle row errors explicitly. The report contains the selected records, receipts, and exact next checkpoint.
5. Deliver rows idempotently and adopt the returned checkpoint. Release the namespace slot only after the cycle is durable.

If downstream delivery is interrupted, retry from the saved report rather than silently moving to a new batch. Use (namespace, record_id) as a row deduplication key and retain batchId/request_digest for report replay. Exact retries can be delivered more than once. No exactly-once delivery guarantee is made.

The checkpoint digest detects accidental edits and links consecutive checkpoints. It is not a signature, authorization mechanism, or server-side lock. Keep checkpoints private; they contain post identities and observation history. Do not reuse one across campaigns or tenants.

### Output artifacts

- Dataset: only selected parent rows
- REPORT: complete report, receipts, selected records, and next checkpoint
- CHECKPOINT: the next caller-owned checkpoint as a separate convenience record
- OUTPUT: publication manifest, written as in_progress before artifacts are changed and complete only after all output steps finish

A failed/interrupted run may leave REPORT, a partial dataset, or CHECKPOINT. None is permission to advance automatically. Require the successful run and matching complete OUTPUT manifest first. Dataset publication, report publication, and checkpoint publication are separate operations.

### New-only and date semantics

New means not previously selected in the supplied checkpoint, not recently published on Instagram. An old unseen post is eligible. Updating likes, caption, or children on an already-selected identity does not make it new. This is not a change feed.

first_seen_at records the first valid observation time supplied by you, including observations excluded by the date window. first_selected_at records first inclusion in a cleanup report. Excluded posts remain eligible when a later date window widens.

since is inclusive; until is exclusive. Date-only boundaries use timezone, default UTC. Naive source timestamps need assumeNaiveTimezone explicitly. Ambiguous/nonexistent daylight-saving times remain errors. Epoch numbers use seconds unless milliseconds is explicitly selected. Missing timestamp with an active date filter is an error.

Shortcodes are case-sensitive. A shared ID/shortcode alias can bridge later observations without changing the original record ID. Two observations with no shared alias cannot be proven identical; conflicting existing identities are never merged automatically.

### Supported fields

id, shortCode, url, type, productType, timestamp, takenAtTimestamp, ownerId, ownerUsername, ownerFullName, caption, displayUrl, videoUrl, audioUrl, inputUrl, isPinned, likesCount, commentsCount, videoViewCount, videoPlayCount, and childPosts. Upstream error/errorDescription markers produce error receipts. The same allowed-field projection applies to supplied children.

Top-level parent fields are normalized by the tested core. Children retain their supplied allowed fields; they are not promoted to independently selected parent records. Omitted parent and child field names appear under receipt.omissions. No inference is made that a coauthor-owned post belongs to the queried profile.

### Bounds and stopping behavior

- Total input: 8 MiB; at most 5,000 input rows
- Each source row: 256 KiB; at most 100 children per supplied parent; nesting depth 64
- Checkpoint: at most 20,000 identities and 6 MiB
- Full report: at most 8 MiB under a conservative pretty-ASCII JSON bound
- Dataset: at most 8 MiB including the enclosing array, and 2 MiB per parent under a conservative JSON encoding check
- Each dataset upload batch: at most 4 MiB, including array brackets and separators
- Each OUTPUT progress/completion manifest: at most 16 KiB
- Actor memory allocation: fixed at 512 MiB; a successful bounded cleanup is still required before checkpoint adoption

All outgoing artifacts and dataset batches are size-checked before any output write. Oversized reports fail before publishing even an in-progress marker. Limits apply together. A large checkpoint leaves less room for new input and report records. Five thousand rows is a ceiling, not a guarantee that every 5,000-row export will fit. JSON formatting/encoding can make a byte cap bind before a row cap. Individual malformed/oversized source rows receive errors. Whole-input, checkpoint, report, or dataset capacity failure rejects the cleanup without returning an adoptable checkpoint. No silent truncation, eviction, history reset, migration, or automatic checkpoint merge occurs.

A completed report can include row errors, and a valid repeat can contain zero selected posts. Read counts and receipts. Data freshness, source completeness, media URL longevity, and content permissions remain your/source responsibilities.

### Privacy, storage, and rights

Submit only exports you are authorized to process and disclose to the hosting provider. The sole exception to the explicit rights acknowledgment is the bundled fictional demo: its complete input, including namespace, batch ID, observation time, posts, UTC timezone and seconds epoch unit, must remain unchanged with rightsAcknowledged=false. Missing or added fields, a checkpoint, altered options or changed content require an explicit true acknowledgment; there is no general demo mode. Public availability alone does not establish permission to reuse content. For your own inputs, the rights acknowledgment records your confirmation; the cleaner does not verify ownership or grant a license. Do not submit passwords, cookies, access tokens, private messages/comments, or sensitive personal data. Remove secrets from URL query strings before upload; this is not a secret-redaction tool.

On Apify, your original INPUT and the generated REPORT, CHECKPOINT, OUTPUT, selected dataset, and any COMPLETION_BILLING attempt journal are stored with the run. Omitting a field from output does not delete it from the original INPUT. Captions, owner details, post/media URL references, identities, and observation history can therefore remain in storage. The application uses Apify storage and does not send these records to another data provider or fetch referenced media.

This Actor does not automatically erase or name storages. Retention follows your Apify plan/settings; named storages can persist indefinitely. Save the report/checkpoint you need, then manage deletion of both the input/output key-value store and dataset in Apify Console. Removing a downstream spreadsheet copy does not remove the stored input or report. See [Apify storage and retention](https://docs.apify.com/storage).

Treat dataset IDs, storage IDs, download links, and checkpoints as confidential: some storage read links work for anyone who has the link. Never post them, tokens, or real customer exports in public issues or examples. Share a small synthetic reproduction for support. See [Apify storage sharing](https://docs.apify.com/storage/share).

### Pricing and completion policy

USD 0.05 buys one verified completed bounded cleanup report. The Console may display this as USD 50 per 1,000 completed cleanup reports; each completed report is one unit, not each post. The configured price has only `cleanup-report-completed`, with no startup, default-dataset-item or per-post developer event, no tier discounts, and platform usage during the run included. Reading or writing the dataset after the run may incur standard Apify platform costs, separate from the completion fee; see [Apify post-run storage costs](https://docs.apify.com/actors/running/actors-in-store). Always check the actual run pricing panel before starting. The Actor rejects eligible work if its effective completion event/price or available budget does not match.

A report must contain at least one valid processed input row and pass every row/byte/identity/child limit. Valid zero-new replays, date-filtered reports and mixed valid/error reports can be billed because a comparison/report was completed. The unchanged fictional demo also produces a completed report and follows the same USD 0.05 event pricing; the acknowledgment exception is not a free-run offer. Empty, all-error, invalid, over-cap and incompletely delivered work triggers no explicit completion event. Rich exports may fit fewer than 5,000 rows. This Actor does not acquire data; an export provider's separate service is outside this cleanup fee.

The fee is attempted only after REPORT, selected dataset rows, CHECKPOINT and complete OUTPUT are stored and read back successfully. Success is based on the SDK reporting one charged event, even when that uses the last affordable USD 0.05 and the SDK reports that the limit is reached. The platform may stop a run at its limit before a later journal update. A complete OUTPUT records delivery, not payment settlement or eventual run success.

A new run is a separate potentially billable invocation, even with the same input. For normal continuation, use a new batchId and the previous successful checkpoint; a zero-new continuation still qualifies. Do not automatically rerun a failed or timed-out paid attempt. There is no permanent cross-run free-retry, atomic delivery/payment, or exactly-once guarantee.

### Billing journal, deadlines and recovery

The Billing attempt journal tab exposes COMPLETION_BILLING for eligible attempts:

- `delivery_started`: a prepared attempt began publication; no completion charge call is confirmed by this state.
- `charge_pending`: the charge call may or may not have committed. Treat it as unknown; do not retry automatically.
- `charged`: the SDK reported one completion event and this local journal was saved. Platform charge history is authoritative.
- `not_charged`: the call reported zero; inspect the run and platform history before deciding how to recover.

Empty/all-error reports normally have no completion journal. Missing or stale journal data is not proof that a remote charge did not happen. An existing journal blocks another eligible attempt using that storage. It is not a lock or shared billing ledger. Preserve all artifacts after failure, inspect OUTPUT/report/checkpoint digests and the platform charge record, and request a charge review through Actor Issues or Apify support before a new run if the outcome is unclear. A failure after delivery or charge may still have a fee; no unconditional refund or free retry is promised.

Source deadlines are independent of the platform's overridable run-timeout default: a 90-second cooperative whole-lifecycle target, a nested 60-second charge target, and a 120-second process watchdog. The watchdog can terminate ordinary stalled work without cleanup, preserving only data already persisted. These mechanisms cannot prove remote rollback, defeat process suspension/native GIL stalls, or bound all billed container lifetime. The Actor remains fixed at 512 MiB; all input/output caps still apply.

### Support and charge review

Open [this Actor’s Issues tab](https://console.apify.com/actors/ARR6RFp9goMr1ceCu/info/issues) in Apify Console for support. Provide a sanitized report with the run ID, build number and static error code, plus a short description using synthetic examples if needed. Issues can be seen by the Actor author, platform admins and other users, so never post raw exports, INPUT, checkpoints, dataset/storage IDs, download links, credentials or tokens. If run metadata is sensitive, contact Apify support instead of posting it in an issue.

If a charge seems incorrect, contact the author through Actor Issues or contact Apify support. Ask for the platform charge record and any `charge_pending` state to be reviewed before retrying. Charge review and refund requests follow the applicable Apify process and terms; no automatic refund or free retry is promised. See [Apify's support and charge guidance](https://docs.apify.com/actors/running/actors-in-store).

### Exact fictional Input JSON

This complete JSON matches the Actor's fictional Input prefill. Keep all fields and values unchanged when rightsAcknowledged is false, including the fixed observation time. Removing or adding an optional field, changing an option, or adding a checkpoint leaves the narrow exception. The example contains deliberately unsupported extra fields so the receipts can show their omission; it does not grant support for arbitrary fields. Its completed report is fee-eligible at USD 0.05.

```json
{
  "namespace": "demo-campaign",
  "batchId": "demo-001",
  "observedAt": "2026-10-13T12:00:00Z",
  "posts": [
    {
      "id": "3333333333333333333",
      "shortCode": "CygCarousel",
      "url": "https://www.instagram.com/p/CygCarousel/?example=synthetic",
      "type": "Sidecar",
      "timestamp": "2026-10-12T10:00:00Z",
      "caption": "Synthetic carousel 🌿",
      "likesCount": -1,
      "commentsCount": 0,
      "ownerUsername": "cygnix_example",
      "childPosts": [
        {
          "id": "child-synthetic-1",
          "type": "Image",
          "displayUrl": "https://example.invalid/photo.jpg",
          "customField": {
            "retained": true
          }
        },
        {
          "id": "child-synthetic-2",
          "type": "Video",
          "videoUrl": "https://example.invalid/video.mp4"
        }
      ]
    },
    {
      "id": "4444444444444444444",
      "shortCode": "CygReel",
      "url": "https://www.instagram.com/p/CygReel/",
      "type": "Video",
      "productType": "clips",
      "timestamp": "2026-10-11T12:00:00+02:00",
      "likesCount": 0,
      "videoViewCount": null,
      "videoPlayCount": 72,
      "audioUrl": "https://example.invalid/audio.mp3"
    },
    {
      "id": "5555555555555555555",
      "shortCode": "CygImage",
      "type": "Image",
      "timestamp": "2026-10-12T08:00:00Z",
      "caption": "Example image without supplied engagement counts",
      "sourceExtra": {
        "retained": true
      }
    }
  ],
  "timezone": "UTC",
  "epochUnit": "seconds",
  "rightsAcknowledged": false
}
```

### Historical technical verification

Source version 0.3.2. On 13 October 2026, the exact unchanged fictional prefill completed on build 0.0.8 with three selected parent records, matching report/dataset/checkpoint artifacts and one completion event. Earlier build 0.0.6 covered the small input, zero-new checkpoint replay, invalid namespace and bounded 5,000-row cases. Those earlier cases were not rerun on build 0.0.8; they are historical coverage, not new checks for this documentation update. These were owner technical tests and do not demonstrate paid-customer adoption or revenue.

### Local run for developers with source

These commands require a local copy of the source package. They are developer reference instructions for people who already have that package; the hosted quick start above needs no local checkout. The package includes actor_samples/INPUT.json and actor_samples/REPEAT_INPUT.json.

Pure local, standard-library processing:

```sh
python -m cygnix_ig_actor.local --input actor_samples/INPUT.json --output /tmp/cleanup-first.json
python -m cygnix_ig_actor.local --input actor_samples/REPEAT_INPUT.json --output /tmp/cleanup-repeat.json
```

The samples select 3 and 0 parent records respectively. The second fixture contains the first fixture's checkpoint. Do not paste a real customer's checkpoint into a public example.

The normal SDK entrypoint intentionally requires the exact configured completion pricing for eligible work. For a free local transformation, use the pure local command above. Developers can exercise the paid entrypoint with the included isolated synthetic-PPE test harness; it needs no account token and bills nothing. Never point tests at customer or production storage.

Docker packaging and platform schemas are included. The normal entrypoint verifies complete output artifacts and exact ordered dataset contents before attempting the completion fee. No n8n/Make/Sheets end-to-end certification is claimed.

# Actor input Schema

## `namespace` (type: `string`):

Use one stable namespace per independent campaign. Prior checkpoint namespace must match exactly.

## `batchId` (type: `string`):

Use a distinct batch ID for each new cleanup. Retain it when retrying the exact original request and checkpoint.

## `observedAt` (type: `string`):

Required offset-aware ISO timestamp. Keep this unchanged across retries; it defines local first-seen time.

## `posts` (type: `array`):

Paste authorized Image, Video, or Sidecar post objects, up to 5,000 rows. This Actor does not fetch Instagram. Input and full report are each capped at 8 MiB; all byte caps apply together and may bind before 5,000 rows. Dataset uploads are split below 4 MiB. Comments and unsupported fields are omitted with receipts.

## `checkpoint` (type: `object`):

Pass the complete CHECKPOINT from the previous successful cleanup in this namespace. At most 20,000 identities and 6 MiB; checkpoint history also uses space within the 8 MiB report. Omit or use null only for deliberately new history. Serialize the entire read-process-deliver-adopt cycle.

## `since` (type: `string`):

Optional date/datetime. Date-only uses the timezone below; all supplied rows are checked.

## `until` (type: `string`):

Optional EXCLUSIVE date/datetime. To include October 12, use October 13.

## `timezone` (type: `string`):

IANA timezone for date-only/naive filter boundaries.

## `assumeNaiveTimezone` (type: `string`):

Optional explicit IANA timezone for source timestamps without an offset. DST gaps/folds remain errors.

## `epochUnit` (type: `string`):

Unit for numeric source timestamps; never guessed per row.

## `rightsAcknowledged` (type: `boolean`):

Confirm you are authorized to use your supplied exports, captions, and URL references. Only the exact unchanged bundled fictional demo may run while this remains false; every changed input requires true. Do not submit credentials, private comments, or unauthorized data.

## Actor input object example

```json
{
  "namespace": "demo-campaign",
  "batchId": "demo-001",
  "observedAt": "2026-10-13T12:00:00Z",
  "posts": [
    {
      "id": "3333333333333333333",
      "shortCode": "CygCarousel",
      "url": "https://www.instagram.com/p/CygCarousel/?example=synthetic",
      "type": "Sidecar",
      "timestamp": "2026-10-12T10:00:00Z",
      "caption": "Synthetic carousel 🌿",
      "likesCount": -1,
      "commentsCount": 0,
      "ownerUsername": "cygnix_example",
      "childPosts": [
        {
          "id": "child-synthetic-1",
          "type": "Image",
          "displayUrl": "https://example.invalid/photo.jpg",
          "customField": {
            "retained": true
          }
        },
        {
          "id": "child-synthetic-2",
          "type": "Video",
          "videoUrl": "https://example.invalid/video.mp4"
        }
      ]
    },
    {
      "id": "4444444444444444444",
      "shortCode": "CygReel",
      "url": "https://www.instagram.com/p/CygReel/",
      "type": "Video",
      "productType": "clips",
      "timestamp": "2026-10-11T12:00:00+02:00",
      "likesCount": 0,
      "videoViewCount": null,
      "videoPlayCount": 72,
      "audioUrl": "https://example.invalid/audio.mp3"
    },
    {
      "id": "5555555555555555555",
      "shortCode": "CygImage",
      "type": "Image",
      "timestamp": "2026-10-12T08:00:00Z",
      "caption": "Example image without supplied engagement counts",
      "sourceExtra": {
        "retained": true
      }
    }
  ],
  "timezone": "UTC",
  "epochUnit": "seconds"
}
```

# Actor output Schema

## `records` (type: `string`):

Default parent-level dataset table. The dataset may be partial after an interrupted run; verify the completion manifest before consumption.

## `completion` (type: `string`):

Native file collection for OUTPUT. Require a SUCCEEDED run, delivery_status=complete, and matching report/checkpoint digests before checkpoint adoption. The JSON record remains available through the KVS records/OUTPUT API.

## `report` (type: `string`):

Native file collection for REPORT, containing records, receipts, and the next checkpoint. Use the collection file controls to access or download it; API clients can read the KVS records/REPORT record.

## `checkpoint` (type: `string`):

Native file collection for CHECKPOINT. Do not adopt it until the run and completion manifest pass the documented checks. API clients can read the KVS records/CHECKPOINT record.

## `billing` (type: `string`):

Support/recovery state for the completion attempt. charge_pending is ambiguous: do not retry automatically. This journal is not proof of payment or rollback; check platform charge history. Empty/all-error work has no completion journal.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "namespace": "demo-campaign",
    "batchId": "demo-001",
    "observedAt": "2026-10-13T12:00:00Z",
    "posts": [
        {
            "id": "3333333333333333333",
            "shortCode": "CygCarousel",
            "url": "https://www.instagram.com/p/CygCarousel/?example=synthetic",
            "type": "Sidecar",
            "timestamp": "2026-10-12T10:00:00Z",
            "caption": "Synthetic carousel 🌿",
            "likesCount": -1,
            "commentsCount": 0,
            "ownerUsername": "cygnix_example",
            "childPosts": [
                {
                    "id": "child-synthetic-1",
                    "type": "Image",
                    "displayUrl": "https://example.invalid/photo.jpg",
                    "customField": {
                        "retained": true
                    }
                },
                {
                    "id": "child-synthetic-2",
                    "type": "Video",
                    "videoUrl": "https://example.invalid/video.mp4"
                }
            ]
        },
        {
            "id": "4444444444444444444",
            "shortCode": "CygReel",
            "url": "https://www.instagram.com/p/CygReel/",
            "type": "Video",
            "productType": "clips",
            "timestamp": "2026-10-11T12:00:00+02:00",
            "likesCount": 0,
            "videoViewCount": null,
            "videoPlayCount": 72,
            "audioUrl": "https://example.invalid/audio.mp3"
        },
        {
            "id": "5555555555555555555",
            "shortCode": "CygImage",
            "type": "Image",
            "timestamp": "2026-10-12T08:00:00Z",
            "caption": "Example image without supplied engagement counts",
            "sourceExtra": {
                "retained": true
            }
        }
    ],
    "timezone": "UTC",
    "epochUnit": "seconds",
    "rightsAcknowledged": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("cygnix_labs/cygnix-instagram-export-cleaner").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "namespace": "demo-campaign",
    "batchId": "demo-001",
    "observedAt": "2026-10-13T12:00:00Z",
    "posts": [
        {
            "id": "3333333333333333333",
            "shortCode": "CygCarousel",
            "url": "https://www.instagram.com/p/CygCarousel/?example=synthetic",
            "type": "Sidecar",
            "timestamp": "2026-10-12T10:00:00Z",
            "caption": "Synthetic carousel 🌿",
            "likesCount": -1,
            "commentsCount": 0,
            "ownerUsername": "cygnix_example",
            "childPosts": [
                {
                    "id": "child-synthetic-1",
                    "type": "Image",
                    "displayUrl": "https://example.invalid/photo.jpg",
                    "customField": { "retained": True },
                },
                {
                    "id": "child-synthetic-2",
                    "type": "Video",
                    "videoUrl": "https://example.invalid/video.mp4",
                },
            ],
        },
        {
            "id": "4444444444444444444",
            "shortCode": "CygReel",
            "url": "https://www.instagram.com/p/CygReel/",
            "type": "Video",
            "productType": "clips",
            "timestamp": "2026-10-11T12:00:00+02:00",
            "likesCount": 0,
            "videoViewCount": None,
            "videoPlayCount": 72,
            "audioUrl": "https://example.invalid/audio.mp3",
        },
        {
            "id": "5555555555555555555",
            "shortCode": "CygImage",
            "type": "Image",
            "timestamp": "2026-10-12T08:00:00Z",
            "caption": "Example image without supplied engagement counts",
            "sourceExtra": { "retained": True },
        },
    ],
    "timezone": "UTC",
    "epochUnit": "seconds",
    "rightsAcknowledged": False,
}

# Run the Actor and wait for it to finish
run = client.actor("cygnix_labs/cygnix-instagram-export-cleaner").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "namespace": "demo-campaign",
  "batchId": "demo-001",
  "observedAt": "2026-10-13T12:00:00Z",
  "posts": [
    {
      "id": "3333333333333333333",
      "shortCode": "CygCarousel",
      "url": "https://www.instagram.com/p/CygCarousel/?example=synthetic",
      "type": "Sidecar",
      "timestamp": "2026-10-12T10:00:00Z",
      "caption": "Synthetic carousel 🌿",
      "likesCount": -1,
      "commentsCount": 0,
      "ownerUsername": "cygnix_example",
      "childPosts": [
        {
          "id": "child-synthetic-1",
          "type": "Image",
          "displayUrl": "https://example.invalid/photo.jpg",
          "customField": {
            "retained": true
          }
        },
        {
          "id": "child-synthetic-2",
          "type": "Video",
          "videoUrl": "https://example.invalid/video.mp4"
        }
      ]
    },
    {
      "id": "4444444444444444444",
      "shortCode": "CygReel",
      "url": "https://www.instagram.com/p/CygReel/",
      "type": "Video",
      "productType": "clips",
      "timestamp": "2026-10-11T12:00:00+02:00",
      "likesCount": 0,
      "videoViewCount": null,
      "videoPlayCount": 72,
      "audioUrl": "https://example.invalid/audio.mp3"
    },
    {
      "id": "5555555555555555555",
      "shortCode": "CygImage",
      "type": "Image",
      "timestamp": "2026-10-12T08:00:00Z",
      "caption": "Example image without supplied engagement counts",
      "sourceExtra": {
        "retained": true
      }
    }
  ],
  "timezone": "UTC",
  "epochUnit": "seconds",
  "rightsAcknowledged": false
}' |
apify call cygnix_labs/cygnix-instagram-export-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cygnix_labs/cygnix-instagram-export-cleaner"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ARR6RFp9goMr1ceCu/builds/zqP3adomMipcemWHc/openapi.json
