{
  "openapi": "3.0.1",
  "info": {
    "title": "Dataset Diff & Change Detector",
    "description": "Returns what was added, removed or changed between two Apify datasets, files or Google Sheets by URL, or JSON arrays, matched by key, field by field, or since its own last run (snapshot mode). CSV/Excel diff report, named-dataset change log, webhook delivery. Agent-ready: pay per event (x402, MCP).",
    "version": "0.1",
    "x-build-id": "STEZzJ5IdHIiQM5R0"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/nerolabs~dataset-diff-detector/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-nerolabs-dataset-diff-detector",
        "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/nerolabs~dataset-diff-detector/runs": {
      "post": {
        "operationId": "runs-sync-nerolabs-dataset-diff-detector",
        "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/nerolabs~dataset-diff-detector/run-sync": {
      "post": {
        "operationId": "run-sync-nerolabs-dataset-diff-detector",
        "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": {
          "oldDatasetId": {
            "title": "Old dataset (the 'before' snapshot)",
            "type": "string",
            "description": "Pick an existing Apify dataset holding the earlier version of your data. Use this OR 'Old file URL' OR 'Old data (inline)' below; leave all three empty when a 'Snapshot name' is set further down. Declaring it this way is what lets this Actor run with limited permissions: it may read the dataset you point at, and nothing else on your account."
          },
          "oldFileUrl": {
            "title": "Old file URL (CSV, Excel, JSON or Google Sheet)",
            "type": "string",
            "description": "Instead of a dataset, download the 'before' snapshot from a public link: a CSV or TSV file, an Excel .xlsx file (first sheet, header row), a JSON array or JSON Lines file, or a Google Sheet (paste the normal sheet link, sharing set to 'Anyone with the link can view'). The format is detected automatically. Up to 100 MB. Never charged. Ignored when 'Old dataset' is set."
          },
          "oldData": {
            "title": "Old data (inline)",
            "type": "array",
            "description": "A JSON array of records for the 'before' snapshot, for ad-hoc data instead of a dataset ID or file URL."
          },
          "newDatasetId": {
            "title": "New dataset (the 'after' snapshot)",
            "type": "string",
            "description": "Pick an existing Apify dataset holding the newer version of your data. Use this OR 'New file URL' OR 'New data (inline)' below."
          },
          "newFileUrl": {
            "title": "New file URL (CSV, Excel, JSON or Google Sheet)",
            "type": "string",
            "description": "Instead of a dataset, download the 'after' snapshot from a public link (same formats as above). Combined with a 'Snapshot name' below this watches a Google Sheet or a published CSV for changes: every run reports only what changed since the last one. Never charged. Ignored when 'New dataset' is set."
          },
          "newData": {
            "title": "New data (inline)",
            "type": "array",
            "description": "A JSON array of records for the 'after' snapshot, for ad-hoc data instead of a dataset ID or file URL."
          },
          "fileFormat": {
            "title": "File format (for file URLs)",
            "enum": [
              "auto",
              "csv",
              "tsv",
              "json",
              "jsonl",
              "xlsx"
            ],
            "type": "string",
            "description": "Only needed if automatic detection gets a file URL's format wrong. Applies to both file URLs.",
            "default": "auto"
          },
          "snapshotName": {
            "title": "Snapshot name (compare against this Actor's previous run)",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$",
            "type": "string",
            "description": "Optional. 1 to 63 letters, digits, dashes or underscores, for example 'shop-prices'. With a name set you can leave 'Old dataset' and 'Old data' empty: the first run saves the baseline and reports nothing, every later run compares the new data against the saved snapshot and then replaces it. Fill 'Old dataset' or 'Old data' as well on the first run to seed the baseline without a wasted run. An empty new snapshot never overwrites a saved baseline. Snapshots live in a key-value store named 'nerolabs-dataset-diff-snapshots' in your own account."
          },
          "resetSnapshot": {
            "title": "Reset snapshot",
            "type": "boolean",
            "description": "Optional. Turn on for one run to forget whatever is saved under the snapshot name above: this run compares nothing (unless you also supply an old snapshot) and saves the new data as a fresh baseline. Turn it off again afterwards.",
            "default": false
          },
          "keyFields": {
            "title": "Key field(s)",
            "type": "array",
            "description": "The field name(s) that uniquely identify a row across both snapshots (e.g. 'sku', 'id', or 'email'). This is what lets the Actor tell 'this row changed' apart from 'this row was removed and a different one was added'. Leave empty to match rows by their full content instead (fine for simple lists, but then any change to any field makes a row look like a remove+add pair rather than a 'changed' row).",
            "items": {
              "type": "string"
            }
          },
          "compareFields": {
            "title": "Fields to compare",
            "type": "array",
            "description": "Optional. Only compare these specific fields when deciding if a matched row changed. Leave empty to compare every field except any listed in 'Fields to ignore' below.",
            "items": {
              "type": "string"
            }
          },
          "ignoreFields": {
            "title": "Fields to ignore",
            "type": "array",
            "description": "Optional. Field names to exclude from comparison, useful for things like a 'scrapedAt' or 'lastChecked' timestamp that always differs between runs and would otherwise mark every row as changed.",
            "items": {
              "type": "string"
            }
          },
          "includeUnchanged": {
            "title": "Include unchanged rows in the output",
            "type": "boolean",
            "description": "When on, rows present in both snapshots with no real difference are also written to the output dataset (charged at a much lower rate, see Pricing). Off by default, since a diff tool's whole point is usually just the things that changed.",
            "default": false
          },
          "maxItems": {
            "title": "Max rows to process",
            "type": "integer",
            "description": "Safety cap on how many rows from the larger of the two snapshots to process. Leave at 0 for no cap (up to the Actor's own 100,000-row hard limit).",
            "default": 0
          },
          "outputDatasetName": {
            "title": "Also append to a named dataset",
            "type": "string",
            "description": "Optional. A name (3 to 63 letters, digits or hyphens, e.g. 'price-change-log'). Every run's added, removed and changed rows are appended to a dataset with this name in your account, created on the first run, so a scheduled watch builds one running change log instead of one dataset per run. Not charged."
          },
          "exportFormats": {
            "title": "Export diff report as file",
            "type": "array",
            "description": "Optional. Generate a real downloadable diff report on top of the dataset. Leave empty to skip file export entirely.",
            "items": {
              "type": "string",
              "enum": [
                "csv",
                "xlsx"
              ],
              "enumTitles": [
                "CSV",
                "Excel (.xlsx)"
              ]
            },
            "default": []
          },
          "webhookUrl": {
            "title": "Webhook URL (optional)",
            "type": "string",
            "description": "Optional. If set, the diff rows (plus the added/removed/changed counts, snapshot details and download links) are POSTed here as JSON on every run, including a run with no changes, so a scheduled watch always reports back to Slack, Zapier, Make, n8n or your own endpoint. Only charged when the endpoint actually confirms receipt (HTTP 2xx); a failed delivery is reported as a warning in the run's output and costs nothing."
          }
        }
      },
      "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
                  }
                }
              },
              "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}