{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Sheets Sync — Reliable Import & Export",
    "description": "Reliable Google Sheets import & export: send Apify datasets or JSON to a sheet (append, replace, upsert with merge, dedupe) or import a sheet to a dataset. Auto-grows the grid, handles schema drift, retries rate limits, clear error messages.",
    "version": "0.1",
    "x-build-id": "AqszE9yxoZLYUBUtB"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/gravelly_caladium~google-sheets-sync/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-gravelly_caladium-google-sheets-sync",
        "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/gravelly_caladium~google-sheets-sync/runs": {
      "post": {
        "operationId": "runs-sync-gravelly_caladium-google-sheets-sync",
        "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/gravelly_caladium~google-sheets-sync/run-sync": {
      "post": {
        "operationId": "run-sync-gravelly_caladium-google-sheets-sync",
        "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",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "export",
              "import"
            ],
            "type": "string",
            "description": "\"Export\" writes rows into a Google Sheet. \"Import\" reads a sheet range and pushes each row as a dataset item.",
            "default": "export"
          },
          "serviceAccountJson": {
            "title": "Google service account JSON (secret)",
            "type": "string",
            "description": "Paste the full JSON key file downloaded for a Google Cloud service account with the Sheets API enabled. Share the target spreadsheet with the service account's client_email as Editor first — see the README setup guide. Leave blank when dryRun is enabled."
          },
          "spreadsheetId": {
            "title": "Spreadsheet ID",
            "type": "string",
            "description": "The ID from the sheet's URL: docs.google.com/spreadsheets/d/<THIS_PART>/edit. Not required when dryRun is enabled."
          },
          "sheetName": {
            "title": "Sheet (tab) name",
            "type": "string",
            "description": "Name of the worksheet tab to read from or write to.",
            "default": "Sheet1"
          },
          "exportMode": {
            "title": "Export write mode",
            "enum": [
              "append",
              "replace",
              "upsert",
              "dedupe"
            ],
            "type": "string",
            "description": "How to write rows in export mode. Append: add rows after existing data. Replace: clear the sheet and write fresh. Upsert: update rows matching Key column(s), append the rest. Dedupe: like append, but skip rows whose key already exists in the sheet or earlier in the same run.",
            "default": "append"
          },
          "keyColumns": {
            "title": "Key column(s)",
            "type": "array",
            "description": "Column/field name(s) that uniquely identify a row. Required for Upsert and Dedupe export modes.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "upsertStrategy": {
            "title": "Upsert strategy",
            "enum": [
              "merge",
              "replace"
            ],
            "type": "string",
            "description": "Only used when exportMode is Upsert. Merge (default): a matched row keeps its existing cell values for any column the new row doesn't include, so a partial update never blanks out other columns. Replace: a matched row is overwritten entirely, exactly like the old behavior -- any column the new row omits is cleared.",
            "default": "merge"
          },
          "sourceDatasetId": {
            "title": "Source dataset ID (export)",
            "type": "string",
            "description": "Export rows from this dataset ID instead of the current run's default dataset. Leave blank to use sourceRunId, inputRows, or the default dataset (checked in that order)."
          },
          "sourceRunId": {
            "title": "Source run ID (export)",
            "type": "string",
            "description": "Export rows from the default dataset of this earlier Actor run ID. Used only if sourceDatasetId is blank."
          },
          "inputRows": {
            "title": "Rows to export (JSON)",
            "type": "array",
            "description": "Export exactly these JSON objects instead of reading a dataset. Handy for testing or for small ad-hoc pushes. Used only if sourceDatasetId and sourceRunId are both blank.",
            "default": []
          },
          "flattenNestedObjects": {
            "title": "Flatten nested objects",
            "type": "boolean",
            "description": "Turn nested objects into dot-separated columns, e.g. address.city. Turn off to JSON-stringify nested values into a single cell instead.",
            "default": true
          },
          "arrayJoinDelimiter": {
            "title": "Array join delimiter",
            "type": "string",
            "description": "Arrays of plain values (strings/numbers/booleans) are joined into one cell with this delimiter. Arrays of objects are always JSON-stringified.",
            "default": ", "
          },
          "importRange": {
            "title": "Import range (A1 notation)",
            "type": "string",
            "description": "Range to read in import mode, e.g. A1:F1000. The sheetName above is used as the tab unless you include a tab name here yourself.",
            "default": "A1:ZZ"
          },
          "headerRow": {
            "title": "Header row number (import)",
            "minimum": 1,
            "type": "integer",
            "description": "Row within the import range that holds column headers. Rows above it are ignored; rows below become dataset items keyed by these headers.",
            "default": 1
          },
          "valueInputOption": {
            "title": "Advanced: value input option (export)",
            "enum": [
              "RAW",
              "USER_ENTERED"
            ],
            "type": "string",
            "description": "How Google Sheets interprets values you write. RAW (default): stored exactly as given -- a number like 54.07 is always stored as that number, regardless of the sheet's locale (e.g. a comma-decimal locale). USER_ENTERED: parsed as if a person typed it into the UI -- needed for a cell that should become a formula (e.g. \"=A1+B1\"), but risks locale-dependent reinterpretation of plain numeric/date-like strings.",
            "default": "RAW"
          },
          "createSheetIfMissing": {
            "title": "Create sheet tab if missing",
            "type": "boolean",
            "description": "If the named sheet tab does not exist yet in the spreadsheet, create it instead of failing (export mode only).",
            "default": true
          },
          "enableResume": {
            "title": "Resume interrupted runs",
            "type": "boolean",
            "description": "Store write progress in the key-value store so that if this run is retried (same spreadsheet, sheet, and mode), it continues from where it left off instead of rewriting or re-appending already-written rows.",
            "default": true
          },
          "maxCellsPerBatch": {
            "title": "Advanced: max cells per API batch",
            "minimum": 100,
            "maximum": 2000000,
            "type": "integer",
            "description": "Self-imposed ceiling on cells written per Sheets API request, well under Google's per-request limits, to keep requests small and retry-friendly.",
            "default": 40000
          },
          "maxBatchBytes": {
            "title": "Advanced: max request payload bytes",
            "minimum": 100000,
            "maximum": 9800000,
            "type": "integer",
            "description": "Self-imposed ceiling on the JSON payload size per Sheets API request, kept under Google's 10 MB request limit.",
            "default": 9000000
          },
          "maxRetries": {
            "title": "Advanced: max retries per API call",
            "minimum": 0,
            "maximum": 10,
            "type": "integer",
            "description": "How many times to retry a single Sheets API call after a 429 (rate limit) or 5xx (server error) response, with exponential backoff and jitter, before giving up.",
            "default": 5
          },
          "dryRun": {
            "title": "Dry run (no Google API calls)",
            "type": "boolean",
            "description": "Run the full pipeline (read input, flatten/coerce rows, batch, resume-check) against an in-memory fake sheet instead of calling Google. Useful for testing without credentials. The dataset gets a summary item describing what would have been written/read.",
            "default": false
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}