Google Cloud Storage
The Google Cloud Storage loader imports data files from a GCS bucket into your warehouse. Zeotap lists the files under a bucket prefix, parses each one, and loads the rows into a table you can model, segment, and activate.
Prerequisites
- A Google Cloud Storage bucket containing your data files.
- A Google Cloud service account with read access to the bucket (the Storage Object Viewer role, which grants
storage.objects.getandstorage.objects.list). - A JSON key for that service account.
- A connected warehouse to load the data into.
Authentication
Zeotap authenticates with a service-account JSON key.
- In the Google Cloud Console, go to IAM & Admin → Service Accounts and create (or select) a service account.
- Grant it the Storage Object Viewer role on the bucket you want to read.
- Open the service account, go to Keys → Add Key → Create new key, choose JSON, and download the key file.
- In Zeotap, add a new Google Cloud Storage loader and paste the full contents of the JSON key into the Service Account JSON field.
The key is stored encrypted and is only used to read objects from the configured bucket.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
| Service Account JSON | Secret | Yes | The full service-account key JSON (must have type: service_account). |
| Bucket | Text | Yes | The GCS bucket name (without the gs:// prefix). |
| Prefix | Text | No | Object name prefix to filter which files are read (e.g. exports/2024/). Leave blank to read the whole bucket. |
| File Format | Select | Yes | The format of the files: CSV, JSON (newline-delimited), Parquet, or Avro. |
| Incremental Cursor | Select | No | For incremental syncs, how new files are detected: File name (default) or Last modified time. Ignored for full-refresh syncs. See Sync Behavior. |
| Destination Table | Text | No | The table this loader writes to inside the target schema. Defaults to files. Give every GCS loader its own name — see Destination Table. |
All files under the prefix that match the selected format are loaded into a single table.
Destination Table
A GCS loader writes to one table: <target schema>.<destination table>, for example cdp_raw.orders_export.
Set a Destination Table on every GCS loader. The field defaults to files, and two loaders that both leave it at the default write to the same table — which means they also share its columns. Whichever runs first creates the table and fixes its schema; the next one, whose files have a different shape, fails its load against a table it did not define. When the column sets happen to overlap it does not even fail — the two datasets interleave in one table with no warning. The same applies to a full refresh loader, which truncates that shared table on every run.
Because of that, a loader whose destination table is already claimed by another loader on the same source and schema is rejected when you save it, and the error names the loader that owns the table.
The name must be usable as a warehouse identifier verbatim — letters, digits and underscores, not starting with a digit. Anything else is rejected rather than silently rewritten. Names are case-insensitive: Orders and orders are one table.
Two things to know before renaming the table on an existing loader:
- Its incremental cursor resets, so the next run re-reads every matching file into the new table. That is usually what you want — the new table is empty — but it is a full pass over the prefix.
- Rows already loaded stay in the old table. Nothing is moved or deleted for you.
Supported Formats
| Format | Notes |
|---|---|
| CSV | The first row is treated as the header. Values are loaded as text. Matches .csv and .csv.gz. |
| JSON | Newline-delimited JSON (one JSON object per line, also called JSONL/NDJSON). Matches .json, .jsonl, .ndjson. A single large JSON array is not supported — use newline-delimited objects. |
| Parquet | Column names and types are read from the file’s embedded schema. Matches .parquet and .pq. |
| Avro | Object Container Files (OCF) with a record-type top-level schema. Matches .avro. |
For CSV and JSON, column types are loaded as text and the schema is inferred from the first file. For Parquet and Avro, the schema is read from the file itself, and Parquet column types are preserved.
Parquet files are read into memory in full (the format requires it), so a single Parquet object must be under 512 MB. Split larger datasets into multiple Parquet files under the prefix. CSV, JSON, and Avro stream and have no per-file size limit.
File Layout
- Place files for one logical dataset under a shared prefix (e.g.
exports/orders/) and point the loader at that prefix. - Every matching file under the prefix is combined into one table. The table’s columns are determined by the first file read — all files under the prefix are assumed to share the same schema. Columns present only in later files are dropped, and columns missing from a later file are loaded as
NULL. - Each loaded row includes a
_gcs_filecolumn recording the source object name, so you can trace rows back to their file.
Sync Behavior
The loader supports both full refresh and incremental syncs.
- Full refresh — each run truncates the target table and reads every matching file under the prefix. Use it when files are rewritten in place or the dataset is small.
- Incremental — each run loads only files that are new since the previous run and appends them. Pick how “new” is decided with the Incremental Cursor setting:
| Cursor | How it detects new files | Best for | Trade-offs |
|---|---|---|---|
| File name (default) | Files whose object name sorts lexicographically after the last file loaded. Already-processed keys are skipped server-side (via GCS StartOffset), so listing stays fast even as history grows. | Date- or sequence-partitioned exports where names always increase, e.g. exports/2026/07/16/part-000.json. | Requires fixed-width, always-increasing names. A counter that overflows its padding (part-999 → part-1000) sorts before the cursor and is silently skipped. Files written with an earlier-sorting name after a sync are missed. |
| Last modified time | Files whose last-modified timestamp is after the newest one loaded. | Any layout — catches renamed, backfilled, or edited files regardless of name, as long as their last-modified time is newer than the previous sync. | No naming assumptions, but there is no server-side time filter, so every run lists the whole prefix (cost grows with total object count). A backfilled file written with an older timestamp is at or before the watermark and is skipped. |
Each incremental run appends; it does not truncate. Switching a loader between the two cursor strategies (or from full refresh to incremental) triggers one reprocessing pass — the stored watermark from the other strategy is not reused — which may duplicate rows already loaded.
To keep the warehouse table current, schedule the loader and write new data to the prefix.
Troubleshooting
| Issue | Resolution |
|---|---|
| ”must be a service account key” | The pasted JSON is not a service-account key. Download a key of type service_account from the service account’s Keys tab. |
| Cannot access bucket | The service account lacks read access. Grant it the Storage Object Viewer role on the bucket. |
| No files found with prefix | No objects under the prefix match the selected format. Check the prefix value and confirm the files have the expected extension (.csv, .json, .parquet, .avro). |
| “destination table conflict” | Another loader on the same source and target schema already writes to that table. Give this loader a different Destination Table (the error names the loader that owns the one you asked for). |
| Load fails on a table this loader did not create | Two loaders are sharing a table — the classic symptom of both leaving Destination Table at its files default. Give each one its own table; see Destination Table. |
| Wrong or missing columns (CSV) | The first row must be a header row. Confirm the file is delimited correctly and the header matches the data. |
| JSON rows skipped | Lines that are not valid JSON objects are skipped. Confirm the file is newline-delimited JSON (one object per line), not a single JSON array. |
| Type mismatches | CSV and JSON values are loaded as text. Cast or transform them in a downstream model. Parquet and Avro preserve their source types. |
Next Steps
- Create a Model over the loaded table.
- Build an Audience from the modeled data.