FacebookCommentsCSVJSONAPI Tutorial

How to Export Facebook Comments to JSON or CSV

Export public Facebook post comments through an API, preserve normalized JSON, and create safe UTF-8 CSV files for Excel, Sheets, analysis, or archives.

SocQUpdated July 19, 20266 min read

Copying Facebook comments into a spreadsheet works for ten comments. It fails when a post has hundreds of comments, authors are missing, text contains emoji or line breaks, and you need to repeat the export tomorrow.

This tutorial uses the SocQ Facebook Comments API to collect comments from public post URLs, preserve normalized JSON, and generate a safe CSV for Excel, Google Sheets, analytics, moderation, or an auditable archive.

Quick answer: Submit public post URLs to POST /v1/facebook/comments, save the returned task ID, poll until completion, and store the normalized comment records as JSON. Create CSV as a derived export, with one comment per row, UTF-8 encoding, explicit parent IDs, and protection against spreadsheet formula injection.

Three Ways to Export Facebook Comments

Manual copy and paste

This is reasonable for a few visible comments and a one-time task. It is not reproducible, does not preserve stable IDs, and makes replies, authors, timestamps, and attachments difficult to audit.

A no-code exporter

No-code tools are convenient when a person needs an Excel file immediately. Check which public URL types are supported, whether replies and attachments are included, how many comments can be exported, and how the provider stores the data.

An API workflow

An API is the better fit for recurring exports, multiple posts, internal products, scheduled reporting, validation, and database loading. It requires more setup but gives you explicit task state, raw JSON, stable transformation code, and repeatable output.

JSON or CSV?

Keep JSON as the source of truth. It preserves nested structures:

  • author identity and profile fields.
  • metrics such as visible likes and reply counts.
  • media attachments.
  • extra references to the source post and parent comment.
  • Nullability and future fields.

CSV is a presentation and interchange format. It is useful for Excel, Google Sheets, BI imports, giveaway review, labeling, and simple analysis, but nested values must be flattened and type information can be lost.

The safest design is:

Public post URLs
      ↓
Facebook Comments API
      ↓
Immutable raw JSON
      ↓
Validated normalized records
      ↓
CSV / warehouse / dashboard

Submit Public Facebook Post URLs

curl -X POST "https://api.socq.ai/v1/facebook/comments" \
  -H "Authorization: Bearer $SOCQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://www.facebook.com/facebook/posts/POST_ID"
    ]
  }'

The current endpoint accepts one or more supported public Facebook post URLs. It does not accept a search term, Page name, or private-group URL as a substitute for a public post.

Before submission:

  1. Require HTTPS and a Facebook hostname.
  2. Resolve URLs in a controlled preprocessing step if you support share links.
  3. Remove tracking parameters when this does not change the post identity.
  4. Deduplicate canonical URLs.
  5. Preserve the original input for provenance.

Poll the Asynchronous Task

The create response contains a task ID. Persist it before polling:

curl "https://api.socq.ai/v1/tasks/$TASK_ID?limit=100" \
  -H "Authorization: Bearer $SOCQ_API_KEY"

Treat queued and running as normal. Back off between requests. On success, process the records in data.results.items; when the response reports has_more, continue with its next_cursor.

Store the input URL, task ID, submission time, terminal status, last committed cursor, and error category. If a worker restarts, resume the existing task rather than creating a duplicate.

Understand the Comment Record

A normalized record resembles:

{
  "id": "1569965031842178_123456789",
  "platform": "facebook",
  "resource": "comments",
  "type": "comment",
  "url": "https://www.facebook.com/facebook/posts/POST_ID",
  "text": "Comment text",
  "author": {
    "id": "100064860875397",
    "username": "facebook",
    "name": "facebook",
    "url": "https://www.facebook.com/facebook/"
  },
  "metrics": {
    "likes_count": 18,
    "replies_count": 2
  },
  "media": [],
  "created_at": "2026-07-07T19:17:33",
  "collected_at": "2026-07-08T08:59:45",
  "extra": {
    "post_id": "1569965031842178",
    "parent_comment_id": null,
    "input_url": "https://www.facebook.com/facebook/posts/POST_ID"
  }
}

Fields can be unavailable. Keep a missing author ID as null, not "unknown". A missing likes count is not zero. replies_count reports a visible count when available; it does not by itself guarantee that every reply body is present in the same result set.

Save the Raw JSON First

Raw storage provides an audit trail and lets you regenerate a corrected CSV without calling the source again. Save:

  • Provider and endpoint version.
  • Task ID and original inputs.
  • Response page and cursor.
  • Collection timestamp.
  • Raw response body or record batch.
  • Validation result and transformation version.

Encrypt sensitive storage, apply an appropriate retention period, and restrict access. “Publicly visible” does not mean “free of privacy obligations.”

Export Comments to CSV with Python

The example below flattens records and protects spreadsheet cells that begin with formula characters:

import csv
import json

FORMULA_PREFIXES = ("=", "+", "-", "@")

def spreadsheet_safe(value):
    if value is None:
        return ""
    text = str(value)
    if text.startswith(FORMULA_PREFIXES):
        return "'" + text
    return text

def flatten(comment):
    author = comment.get("author") or {}
    metrics = comment.get("metrics") or {}
    extra = comment.get("extra") or {}

    return {
        "post_id": extra.get("post_id"),
        "comment_id": comment.get("id"),
        "parent_comment_id": extra.get("parent_comment_id"),
        "author_id": author.get("id"),
        "author_name": spreadsheet_safe(author.get("name")),
        "comment_text": spreadsheet_safe(comment.get("text")),
        "likes_count": metrics.get("likes_count"),
        "replies_count": metrics.get("replies_count"),
        "created_at": comment.get("created_at"),
        "collected_at": comment.get("collected_at"),
        "source_url": extra.get("input_url") or comment.get("url"),
    }

with open("facebook-comments.json", encoding="utf-8") as source:
    comments = json.load(source)

rows = [flatten(comment) for comment in comments]
fields = list(rows[0].keys()) if rows else []

with open("facebook-comments.csv", "w", newline="", encoding="utf-8-sig") as output:
    writer = csv.DictWriter(output, fieldnames=fields)
    writer.writeheader()
    writer.writerows(rows)

utf-8-sig writes a UTF-8 byte-order mark that helps common Excel installations detect Unicode. Python's CSV writer correctly quotes commas, quotes, and embedded line breaks.

Do not create CSV by joining values with commas. A comment can legally contain all of those characters.

Export with Node.js

Use a maintained CSV library rather than a custom string join. The transformation stays the same:

import { writeFile } from "node:fs/promises";
import { stringify } from "csv-stringify/sync";

const unsafe = /^[=+\-@]/;
const safe = (value) => {
  if (value == null) return "";
  const text = String(value);
  return unsafe.test(text) ? `'${text}` : text;
};

const rows = comments.map((comment) => ({
  post_id: comment.extra?.post_id ?? "",
  comment_id: comment.id ?? "",
  parent_comment_id: comment.extra?.parent_comment_id ?? "",
  author_id: comment.author?.id ?? "",
  author_name: safe(comment.author?.name),
  comment_text: safe(comment.text),
  likes_count: comment.metrics?.likes_count ?? "",
  replies_count: comment.metrics?.replies_count ?? "",
  created_at: comment.created_at ?? "",
  collected_at: comment.collected_at ?? "",
  source_url: comment.extra?.input_url ?? comment.url ?? "",
}));

const csv = stringify(rows, { header: true });
await writeFile("facebook-comments.csv", `\uFEFF${csv}`, "utf8");

Pin the CSV dependency, validate the resulting row count, and test comments containing emoji, commas, double quotes, tabs, newlines, and formula prefixes.

Model Comments and Replies

Use comment_id as the preferred record key. parent_comment_id is null for a top-level comment and references a parent when that relationship is available.

Do not use display name as an identity key. Names are neither unique nor permanent. Likewise, do not assume display order is chronological: platforms can rank comments and personalize visible ordering.

For recurring exports, upsert the comment entity and append a collection snapshot for mutable metrics:

comment
  comment_id
  post_id
  parent_comment_id
  author_id
  text
  created_at
  first_seen_at
  last_seen_at

comment_snapshot
  comment_id
  collected_at
  likes_count
  replies_count

Build an Incremental Export

For a daily workflow:

  1. Submit the canonical post URL.
  2. Poll and validate the completed task.
  3. Upsert by comment ID.
  4. Mark every returned record with last_seen_at.
  5. Append metric snapshots only for successful collections.
  6. Generate CSV from the database or validated JSON.
  7. Record counts for submitted, returned, valid, new, updated, and rejected items.

Do not mark a previously seen comment as deleted after one failed or incomplete collection. Require multiple successful observations or a separate verification policy.

Validate the Export

Check:

  • Every record says platform: facebook and resource: comments.
  • IDs are unique within a post when present.
  • Timestamps parse consistently.
  • Counts are non-negative or null.
  • Parent IDs do not point to the same comment.
  • CSV row count equals the number of transformed records.
  • A sample of emoji, multilingual text, quotes, and line breaks round-trips correctly.
  • Every row retains its source URL and collection time.

Compare API output with a small public browser sample, but do not treat the visible browser count as an exact completeness oracle. Comments can be ranked, filtered, deleted, hidden, or changed between observations.

Responsible Use

Collect only public comments needed for a legitimate purpose. Do not bypass login, private groups, audience restrictions, or technical controls. Avoid using comments to infer sensitive traits or make high-impact decisions about people.

Define a lawful basis where required, limit retention, secure exports, control spreadsheet sharing, honor applicable deletion and access rights, and review Meta's current terms and local law. This tutorial is not legal advice.

FAQ

Can I export every comment from a Facebook post?

No provider should promise universal completeness. Availability depends on the public post, visible ordering, source behavior, deletions, and endpoint coverage.

Does the export include replies?

The normalized model can represent parent relationships and visible reply counts. Verify the current endpoint's reply-body coverage for your sample instead of assuming every reply is returned.

Can I export comments from a private group?

Not with this public-data workflow. Do not bypass group privacy or access controls.

Should I choose JSON or CSV?

Keep JSON as the auditable source and generate CSV for spreadsheets and simple imports.

Will emoji survive the export?

Yes, when the entire pipeline uses UTF-8. The examples write UTF-8 CSV with a BOM for Excel compatibility.

Why protect formula prefixes?

Spreadsheet software can interpret comment text beginning with =, +, -, or @ as a formula. Prefixing those cells prevents untrusted text from executing as spreadsheet content.

Can I automate the export every day?

Yes. Persist task state, use backoff, deduplicate by comment ID, append metric snapshots, and generate the file only from validated results.

FACEBOOK API

Test the workflow with a public Facebook URL

Submit public inputs and receive normalized records with traceable source context.

Explore Facebook APIs