{
  "openapi": "3.0.1",
  "info": {
    "title": "Dataset Dedup, Merge & Compare: Only New Items, Reconcile",
    "description": "Merge Apify datasets and remove or merge duplicates (with normalized URLs, phones, domains), keep only items new since earlier runs using persistent memory, or compare two datasets or CSV files and get field-level differences. Limited permissions.",
    "version": "0.1",
    "x-build-id": "bqeakkF39iCYYCXKM"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/neorhapsody~dataset-merge-dedup/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-neorhapsody-dataset-merge-dedup",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/neorhapsody~dataset-merge-dedup/runs": {
      "post": {
        "operationId": "runs-sync-neorhapsody-dataset-merge-dedup",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/neorhapsody~dataset-merge-dedup/run-sync": {
      "post": {
        "operationId": "run-sync-neorhapsody-dataset-merge-dedup",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "mode": {
            "title": "What to do",
            "enum": [
              "dedup",
              "compare"
            ],
            "type": "string",
            "description": "Dedup & merge: combine datasets and remove or merge duplicates (default). Compare & reconcile: match two datasets (or CSV/JSON files) by key and report matched rows, field-level differences, and rows only in A or only in B.",
            "default": "dedup"
          },
          "datasetIds": {
            "title": "Datasets to merge",
            "type": "array",
            "description": "Pick one or more datasets (dataset ID or name; in integrations use {{resource.defaultDatasetId}}). Items are read in the order listed. Picking grants this Actor read access, so private datasets work under limited permissions. Leave empty (and Sources empty) for a free demo on a public sample dataset."
          },
          "sources": {
            "title": "Sources as text: dataset IDs, run IDs or Console URLs",
            "type": "array",
            "description": "Optional. Paste dataset IDs, run IDs, or Apify Console/API URLs of datasets or runs. Note: text gives no access to private data under limited permissions, so private datasets must be picked above; public datasets work here. For anything unreadable you get a clear error record explaining what to pick instead.",
            "items": {
              "type": "string"
            }
          },
          "dedupEnabled": {
            "title": "Remove duplicates",
            "type": "boolean",
            "description": "Remove duplicate items across all selected datasets.",
            "default": true
          },
          "dedupFields": {
            "title": "Dedup by fields",
            "type": "array",
            "description": "Fields that identify a duplicate, e.g. [\"url\"] or [\"title\",\"company\"]. Dot paths for nested fields work (\"location.city\"). Leave empty to compare whole items.",
            "items": {
              "type": "string"
            }
          },
          "keep": {
            "title": "Which duplicate to keep",
            "enum": [
              "first",
              "last",
              "merge"
            ],
            "type": "string",
            "description": "First occurrence: fastest, single streaming pass. Last occurrence: newest data wins (two passes, low memory). Merge duplicates: combine all copies into one item, keeping the most complete value per field (empty fields filled from other copies, lists combined) with _mergedCount, _mergedFrom, _filledFrom and _conflictingFields for source tracking.",
            "default": "first"
          },
          "mergeArrays": {
            "title": "Merge: combine lists",
            "type": "boolean",
            "description": "In merge mode, combine list fields (e.g. emails, phones) from all duplicates without repeats. Off: keep the first non-empty list.",
            "default": true
          },
          "dedupEmptyKeys": {
            "title": "Treat empty keys as duplicates",
            "type": "boolean",
            "description": "Off (default): rows where all dedup fields are empty (e.g. no website) are kept as separate rows, because they can't be matched reliably. On: they count as duplicates of each other, so only one is kept.",
            "default": false
          },
          "caseInsensitive": {
            "title": "Case-insensitive matching",
            "type": "boolean",
            "description": "Treat \"Apify\" and \"apify\" as the same value when comparing dedup fields.",
            "default": true
          },
          "trimWhitespace": {
            "title": "Ignore extra whitespace",
            "type": "boolean",
            "description": "Trim and collapse whitespace before comparing dedup fields.",
            "default": true
          },
          "normalizeDiacritics": {
            "title": "Ignore accents (diacritics)",
            "type": "boolean",
            "description": "Treat \"Café Zürich\" and \"Cafe Zurich\" as the same value.",
            "default": false
          },
          "normalizeUrls": {
            "title": "Normalize URLs",
            "type": "boolean",
            "description": "For values that look like URLs: ignore http/https, www., trailing slash, #fragment, tracking parameters (utm_*, fbclid, gclid…) and query parameter order.",
            "default": false
          },
          "normalizePhones": {
            "title": "Normalize phone numbers",
            "type": "boolean",
            "description": "For values that look like phone numbers: compare digits only, ignoring formatting, leading zeros and country codes (last 9 digits), so \"+60 12-345 6789\" matches \"012-345 6789\".",
            "default": false
          },
          "normalizeDomains": {
            "title": "Match on domain only",
            "type": "boolean",
            "description": "For values that look like URLs, emails or domains: compare only the domain, so \"https://www.acme.com/contact\", \"acme.com\" and \"sales@acme.com\" match. Use it to dedupe companies by website.",
            "default": false
          },
          "excludeDatasetIds": {
            "title": "Only new items: exclude items already in these datasets",
            "type": "array",
            "description": "Optional. Items whose dedup key already exists in these datasets (e.g. the previous run's output) are dropped, so you get only NEW items."
          },
          "memoryName": {
            "title": "Remember across runs (memory name)",
            "type": "string",
            "description": "Optional. Give a name (e.g. \"daily-jobs\") and the Actor remembers every item it has output under that name, in a key-value store named dedup-memory-<name> in your own account. Later runs with the same name output only items never seen before. Ideal for scheduled scrapers and integrations. Works under limited permissions."
          },
          "resetMemory": {
            "title": "Reset memory",
            "type": "boolean",
            "description": "Forget everything remembered under the memory name before this run.",
            "default": false
          },
          "datasetA": {
            "title": "Dataset A (e.g. old / source of truth)",
            "type": "string",
            "description": "Compare mode. First dataset. Leave both sides empty for a free demo (last week's vs this week's sample products)."
          },
          "datasetB": {
            "title": "Dataset B (e.g. new / to check)",
            "type": "string",
            "description": "Compare mode. Second dataset."
          },
          "fileUrlA": {
            "title": "…or file URL A (CSV or JSON)",
            "type": "string",
            "description": "Compare mode. Public URL of a CSV, JSON or JSON Lines file to use as side A instead of a dataset (e.g. a bank export or price list). Max 200 MB."
          },
          "fileUrlB": {
            "title": "…or file URL B (CSV or JSON)",
            "type": "string",
            "description": "Compare mode. Public URL of a CSV, JSON or JSON Lines file to use as side B."
          },
          "compareKeyFields": {
            "title": "Match rows by (key fields)",
            "type": "array",
            "description": "Fields that identify the same record on both sides, e.g. \"id\" or \"sku\". If names differ, write fieldInA=fieldInB, e.g. \"invoiceNo=reference\". Matching uses the case/whitespace/accent/URL/phone/domain options above. Rows are paired one-to-one; extra rows with a repeated key are reported as only in A/B.",
            "items": {
              "type": "string"
            }
          },
          "compareFields": {
            "title": "Fields to compare",
            "type": "array",
            "description": "Optional. Fields to check for differences (fieldInA=fieldInB if names differ). Empty = every field present on both sides except the key fields.",
            "items": {
              "type": "string"
            }
          },
          "ignoreFields": {
            "title": "Ignore these fields",
            "type": "array",
            "description": "Optional. Fields never compared, e.g. scrapedAt or updatedAt.",
            "items": {
              "type": "string"
            }
          },
          "numericTolerance": {
            "title": "Number tolerance (absolute)",
            "minimum": 0,
            "type": "number",
            "description": "Numbers that differ by at most this much count as equal, e.g. 0.01. Numbers are recognised in text too (\"$1,234.50\", \"RM 12\", \"(15.00)\").",
            "default": 0
          },
          "numericTolerancePercent": {
            "title": "Number tolerance (%)",
            "minimum": 0,
            "type": "number",
            "description": "Numbers within this percentage of the larger value count as equal, e.g. 1 for 1%. If both tolerances are set, the larger allowance applies.",
            "default": 0
          },
          "dateToleranceDays": {
            "title": "Date tolerance (days)",
            "minimum": 0,
            "type": "number",
            "description": "Dates within this many days count as equal, e.g. 2 for posting delays. Use ISO dates (2026-10-23) where possible; US-style 10/23/2026 is read as month/day.",
            "default": 0
          },
          "includeMatched": {
            "title": "Output identical rows",
            "type": "boolean",
            "description": "Also output rows that matched with no differences (each is a charged row). Turn off to get only differences and unmatched rows.",
            "default": true
          },
          "filters": {
            "title": "Filters (all must match)",
            "type": "array",
            "description": "Keep only items matching ALL filters. Each filter: {\"field\": \"price\", \"operator\": \"greaterThan\", \"value\": 10}. Operators: equals, notEquals, contains, notContains, greaterThan, lessThan, exists, notExists, regex, in (comma-separated list). String comparisons are case-insensitive.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "title": "Field",
                  "type": "string",
                  "description": "Field name or dot path."
                },
                "operator": {
                  "title": "Operator",
                  "type": "string",
                  "description": "Comparison operator.",
                  "enum": [
                    "equals",
                    "notEquals",
                    "contains",
                    "notContains",
                    "greaterThan",
                    "lessThan",
                    "exists",
                    "notExists",
                    "regex",
                    "in"
                  ]
                },
                "value": {
                  "title": "Value",
                  "type": "string",
                  "description": "Value to compare with (not needed for exists/notExists)."
                }
              },
              "required": [
                "field",
                "operator"
              ]
            }
          },
          "pickFields": {
            "title": "Keep only these fields",
            "type": "array",
            "description": "Optional. Output only these fields (dot paths allowed). Leave empty to keep all fields.",
            "items": {
              "type": "string"
            }
          },
          "omitFields": {
            "title": "Remove these fields",
            "type": "array",
            "description": "Optional. Fields to drop from every item.",
            "items": {
              "type": "string"
            }
          },
          "renameFields": {
            "title": "Rename fields",
            "type": "array",
            "description": "Optional. Old field name -> new field name.",
            "items": {
              "type": "object",
              "required": [
                "key",
                "value"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "title": "Key"
                },
                "value": {
                  "type": "string",
                  "title": "Value"
                }
              }
            }
          },
          "addSourceDatasetField": {
            "title": "Add source dataset ID",
            "type": "boolean",
            "description": "Add a _sourceDatasetId field to each output item.",
            "default": false
          },
          "maxItemsPerDataset": {
            "title": "Max items per dataset",
            "minimum": 0,
            "type": "integer",
            "description": "Read at most this many items from each dataset or side (0 = all). Useful for testing.",
            "default": 0
          },
          "outputDatasetId": {
            "title": "Write to existing dataset",
            "type": "string",
            "description": "Optional. Append results to this dataset instead of the run's default dataset."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}