Firexport logo

FirexportGuides

How to export Firestore data to JSON

Realtime Database has an Export JSON button in the Firebase console. Firestore doesn’t. There are three practical ways to get Firestore documents out as JSON, from the results of one query to a whole collection with its subcollections.

Updated September 24, 2026. Firexport, the extension in method 1, is ours. Methods 2 and 3 don’t need it.

The short answer

  • The results of one query, right now: run it in the console’s Query builder and export it with a browser extension. Method 1
  • A collection with its subcollections, or a repeatable export: a short Admin SDK script. Method 2
  • Every document in a large collection, or you already use BigQuery: managed export with gcloud, then BigQuery to JSON. Method 3
1. Console + extension2. Admin SDK script3. gcloud + BigQuery
SetupInstall a Chrome extensionNode.js and Google Cloud credentialsCloud Storage bucket and BigQuery dataset
Billing enabledNot neededNot neededRequired (Blaze plan)
FileOne JSON arrayOne JSON arrayNewline-delimited JSON, one document per line
SubcollectionsSeparately, as a collection group queryNested under each document (optional)Separately, one table per collection ID
TimestampsAs the console shows them (ISO 8601 with Advanced)ISO 8601BigQuery timestamp text

1. Export from the Firebase console with Firexport

The Firestore page in the Firebase console has a Query builder that runs a query and shows the results in a table, but nothing to download them with. Firexport is a free Chrome extension that adds an Export button next to those results.

  1. Install Firexport from the Chrome Web Store.
  2. In the Firebase console, open Firestore Database and switch from Panel view to Query builder (top right of the Data tab).
  3. Pick the collection under Query scope and Path, add filters or a limit if you want, and click Run.
  4. Open the ⋮ menu next to the Export button and choose Export to JSON. Firexport goes through every page of the results and downloads one JSON file. (The Export button itself saves CSV.)
The Firestore Query builder in the Firebase console, with the Export button Firexport adds above the query results
The Export button and its ⋮ menu sit above the Query builder results.

The file is one JSON array with an object per document: a Document ID key, then the document’s fields. Numbers, booleans and null keep their JSON types, and maps and arrays come out nested. What differs between the free version and Advanced is the values the console only shows as text:

Free — timestamps, geopoints and references as the console displays them:

[
  {
    "Document ID": "2PomjkgoCISvwnlGrypP",
    "name": "firexport",
    "count": 1,
    "tags": ["dev", "export"],
    "settings": { "theme": "dark", "beta": true },
    "createdAt": "August 24, 2026 at 1:16:09 PM UTC+9",
    "location": "[37.5665° N, 126.978° E]",
    "owner": "/users/u1"
  }
]

Advanced (a one-time license) — the same values restored from the data the console has already loaded:

[
  {
    "Document ID": "2PomjkgoCISvwnlGrypP",
    "name": "firexport",
    "count": 1,
    "tags": ["dev", "export"],
    "settings": { "theme": "dark", "beta": true },
    "createdAt": "2026-08-24T04:16:09.012207Z",
    "location": { "latitude": 37.5665, "longitude": 126.978 },
    "owner": "projects/my-app/databases/(default)/documents/users/u1"
  }
]

In the free version, a map or array whose console text can’t be read back as JSON (one the console cut off with “…”, for example) stays a string. Advanced writes those in full. Either way there is no service account key and no billing to set up: it works in the console session you’re already signed in to, and the file is built in your browser. It runs in Chrome and other Chromium-based browsers.

Export your next Firestore query as JSON from the console, without a script or a service account key.

Add to Chrome — free

2. Export with an Admin SDK script

A short Node.js script with the Firebase Admin SDK can export a whole collection, and it can walk into subcollections, which neither of the other methods does. It needs an account that can read Firestore: a project Owner or Editor, or the Cloud Datastore Viewer role.

  1. Install the SDK: npm install firebase-admin
  2. Sign in for local credentials: gcloud auth application-default login. On a server, point GOOGLE_APPLICATION_CREDENTIALS at a service account key instead.
  3. Save the script below as export-to-json.mjs, replace your-project-id, and run node export-to-json.mjs users. Add --subcollections to include each document’s subcollections.
// Usage: node export-to-json.mjs users [--subcollections]   → writes users.json
import { writeFileSync } from 'node:fs';
import { initializeApp } from 'firebase-admin/app';
import {
  getFirestore,
  FieldPath,
  Timestamp,
  GeoPoint,
  DocumentReference,
} from 'firebase-admin/firestore';

initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
const [collection, flag] = process.argv.slice(2);
if (!collection) throw new Error('Usage: node export-to-json.mjs <collection> [--subcollections]');
const withSubcollections = flag === '--subcollections';

// Firestore types become JSON values: timestamps as ISO 8601, references as paths.
function plain(value) {
  if (value instanceof Timestamp) return value.toDate().toISOString();
  if (value instanceof DocumentReference) return value.path;
  if (value instanceof GeoPoint) return { latitude: value.latitude, longitude: value.longitude };
  if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');
  if (Array.isArray(value)) return value.map(plain);
  if (value && typeof value === 'object') {
    return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, plain(v)]));
  }
  return value;
}

// Read a collection 1,000 documents at a time. With --subcollections, each document also gets its
// subcollections under "__collections__", read the same way.
async function readCollection(ref) {
  const docs = [];
  let last;
  while (true) {
    let query = ref.orderBy(FieldPath.documentId()).limit(1000);
    if (last) query = query.startAfter(last);
    const page = await query.get();
    if (page.empty) break;
    for (const doc of page.docs) {
      const row = { documentId: doc.id, ...plain(doc.data()) };
      if (withSubcollections) {
        for (const sub of await doc.ref.listCollections()) {
          row.__collections__ ??= {};
          row.__collections__[sub.id] = await readCollection(sub);
        }
      }
      docs.push(row);
    }
    last = page.docs[page.docs.length - 1];
  }
  return docs;
}

const docs = await readCollection(db.collection(collection));
writeFileSync(`${collection}.json`, JSON.stringify(docs, null, 2));
console.log(`Wrote ${docs.length} documents to ${collection}.json`);
  • With --subcollections, each document that has subcollections gets a __collections__ key holding them, nested as deep as they go. Finding them takes one extra request per document, so large collections take longer.
  • Subcollections under a document that doesn’t exist itself (the console shows its ID in italics) are not reached from the parent collection. Export those by collection group.
  • The script holds everything in memory before it writes the file. For millions of documents, use method 3.

3. Export with gcloud and BigQuery

Firestore’s managed export writes a backup to Cloud Storage in Firestore’s own format, not JSON. BigQuery can load that format and write it back out as JSON. This is the route for large collections, but it needs billing enabled (the Blaze plan) and permission to run exports and write to the bucket. Google’s export and import docs list the roles.

# 1. Export one collection. BigQuery only loads exports made with --collection-ids.
gcloud firestore export gs://YOUR_BUCKET/users-export --collection-ids=users

# 2. Load it into a BigQuery dataset in the same location as the bucket.
bq --location=US mk --dataset firestore_export
bq load --source_format=DATASTORE_BACKUP firestore_export.users \
  gs://YOUR_BUCKET/users-export/all_namespaces/kind_users/all_namespaces_kind_users.export_metadata

# 3. Write it out as newline-delimited JSON and download it.
bq extract --destination_format=NEWLINE_DELIMITED_JSON firestore_export.users gs://YOUR_BUCKET/users.json
gcloud storage cp gs://YOUR_BUCKET/users.json .
  • The file is newline-delimited JSON: one document per line, not one array. To turn it into an array, run jq -s . users.json > users-array.json.
  • Maps and arrays stay nested. Each line also has a __key__ object from the export, which holds the document ID.
  • BigQuery writes at most 1 GB per file. For more, use a wildcard such as gs://YOUR_BUCKET/users-*.json.
  • The export is billed as one document read per document. Delete the export folder when you’re done so it stops using Cloud Storage.

FAQ

Can the Firebase console export Firestore data to JSON?

No. Realtime Database has Export JSON in its data viewer’s menu, but Firestore’s Data tab has no download. The managed export (the Import/Export page in the Google Cloud console, or gcloud firestore export) writes a backup in Firestore’s own format to Cloud Storage, not JSON.

Why does the Firestore REST API return values like {"stringValue": "..."}?

The REST API wraps every field in its type: stringValue, integerValue, timestampValue, mapValue and so on. It is JSON, but not the shape of your documents. The Admin SDK (method 2) unwraps those types for you.

Can I import the JSON back into Firestore?

Not directly. gcloud firestore import only reads managed exports, and none of these JSON files are one. To load a JSON file, write it back with a script (the Admin SDK’s BulkWriter handles large batches). If restoring data is the goal, use the managed export and import instead of JSON.

Does exporting Firestore data cost money?

Every method reads each exported document at least once, and Firestore bills those as document reads. Within the daily free quota that costs nothing. The managed export also needs billing enabled on the project, and its files take up Cloud Storage until you delete them.