5channel Data Services

Formats and delivery

The proposed delivery specification uses newline-delimited JSON (JSONL) or Apache Parquet, partitioned by board and time period where agreed. Records are designed to use stable identifiers so that snapshots and incremental deliveries can be joined and deduplicated.

Delivery method, partitioning, update cadence, filtering and final field list are agreed during scoping. The examples below are illustrative; your export schema is confirmed before delivery.

What is standard, agreed, or illustrative

  • — Standard where confirmed for your engagement: UTF-8 output, agreed field list, delivery manifest with file list and checksums.
  • — Agreed per engagement: board and date scope, redaction profile, update cadence, delivery channel (object storage, scheduled export, delta feed, or dedicated endpoint).
  • — Illustrative on this page: example JSON records, field tables and code snippets. These show the proposed schema, not a guarantee of any particular export without scoping.

Post record schema (proposed)

Illustrative example record. Final fields, normalisation rules and redaction profile are agreed during scoping.

{
  "post_id": "5ch:news4vip:1712345678:0042",
  "thread_id": "5ch:news4vip:1712345678",
  "board_id": "news4vip",
  "board_name_ja": "ニュー速VIP",
  "board_category_ja": "雑談系",
  "post_index": 42,
  "posted_at": "2024-04-05T21:14:38+09:00",
  "posted_at_precision": "second",
  "name_field": "以下、5ちゃんねるからVIPがお送りします",
  "poster_id": "aB3xY9Zk0",
  "body_text": "そういうのはこっちのスレでやってくれ\n>>38 が言ってたやつな",
  "body_normalized": "そういうのはこっちのスレでやってくれ >>38 が言ってたやつな",
  "reply_to": [38],
  "quoted_spans": [
    { "target_post_index": 38, "raw": ">>38" }
  ],
  "char_count": 34,
  "contains_ascii_art": false,
  "redaction_profile": "standard",
  "redactions_applied": 0,
  "source_snapshot_id": "snap-2026-08-01",
  "record_version": 1
}
FieldTypeDescription
post_idstringGlobally stable post identifier
thread_idstringParent thread identifier
board_idstringBoard short code
board_name_jastringBoard display name
board_category_jastringBoard category grouping
post_indexint1-based position within thread
posted_atstring (RFC 3339)Post timestamp, JST, timezone-explicit
posted_at_precisionenumsecond | minute | day — source precision varies by era
name_fieldstringName field as submitted
poster_idstring | nullSource-provided poster ID where present
body_textstringPost body, source form, newlines preserved
body_normalizedstringNFKC-normalised, whitespace-collapsed variant
reply_toint[]Resolved reply targets by post_index
quoted_spansobject[]Raw anchor text and resolved target
char_countintCharacter count of body_text
contains_ascii_artboolHeuristic ASCII-art flag
redaction_profileenumraw | standard | strict — see licensing page
redactions_appliedintCount of redactions in this record
source_snapshot_idstringSnapshot this record was exported from
record_versionintSchema version of this record

Thread record schema (proposed)

Illustrative example record. Thread coverage and edge resolution depend on the agreed scope and source completeness.

{
  "thread_id": "5ch:news4vip:1712345678",
  "board_id": "news4vip",
  "title": "日本語の口語表現について語るスレ",
  "title_normalized": "日本語の口語表現について語るスレ",
  "created_at": "2024-04-05T20:47:58+09:00",
  "last_post_at": "2024-04-06T03:22:11+09:00",
  "post_count": 412,
  "unique_poster_id_count": 87,
  "status": "archived",
  "reply_edges": [[42, 38], [43, 42], [44, 38]],
  "source_snapshot_id": "snap-2026-08-01",
  "record_version": 1
}

reply_edges lists explicit [from_post_index, to_post_index] pairs derived from >>N anchors in the source. This captures explicit reply links, not every conversational relationship. Missing context may occur where anchors are absent or ambiguous.

FieldTypeDescription
thread_idstringThread identifier
board_idstringBoard short code
titlestringThread title
title_normalizedstringNFKC-normalised title
created_atstring (RFC 3339)Thread creation timestamp
last_post_atstring (RFC 3339)Last post timestamp
post_countintTotal posts in thread
unique_poster_id_countintUnique poster IDs
statusenumactive | archived | capacity-closed
reply_edgesint[][][from_post_index, to_post_index] pairs
source_snapshot_idstringSnapshot this record was exported from
record_versionintSchema version

Encoding and normalisation (proposed)

  • — Output is UTF-8. Legacy source encodings are converted at parse time with a documented mapping; conversion failures are recorded rather than silently dropped.
  • — body_text preserves the source form, including full-width characters, kaomoji, and intentional orthographic variation.
  • — body_normalized provides an NFKC-normalised, whitespace-collapsed parallel field where agreed. Both may be delivered; consumers choose.
  • — Reply anchors (>>N, >>N, and historical variants) can be parsed into structured fields while the original text is retained in body_text.
  • — Timestamps are RFC 3339 with explicit JST offset. Where source precision is coarser than one second, posted_at_precision records this rather than fabricating precision.
  • — Identifiers are designed to be stable across snapshots within a defined export. A record's post_id does not change between deliveries of the same export definition.

Snapshots and incremental delivery (where agreed)

A snapshot is a point-in-time export of a defined scope (boards, date range, filters), identified by source_snapshot_id and accompanied by a manifest listing every file, its record count, and its SHA-256 checksum.

A delta is the set of records added, modified, or withdrawn between two snapshots, expressed as typed change records so that a downstream store can be brought forward without a full reload.

Deletion and correction events in the source can be propagated through delta feeds where the licence provides for it. Availability of incremental updates is confirmed during scoping.

# JSONL
import json
with open("posts.jsonl", encoding="utf-8") as f:
    posts = [json.loads(line) for line in f]

# Parquet
import pyarrow.parquet as pq
table = pq.read_table("posts.parquet")
df = table.to_pandas()

# Hive partitions by board and month
part = pq.read_table("5ch_posts/board_id=news4vip/year=2026/month=08/")

# Reconstruct a thread with its reply graph
thread_id = posts[0]["thread_id"]
thread = [p for p in posts if p["thread_id"] == thread_id]
thread.sort(key=lambda p: p["post_index"])
edges = [(p["post_index"], t) for p in thread for t in p["reply_to"]]

Delivery options

Encrypted object storage
Signed download URLs to encrypted shards with manifest and checksums — Standard option
Scheduled snapshots
Periodic full exports of a defined scope — Agreed per engagement
Incremental delta feed
Typed change records between snapshots — Agreed per engagement
Dedicated access endpoint
Programmatic access scoped to the licence — Agreed per engagement
Private repository delivery
Delivery to a customer-controlled private repository — Agreed per engagement

Request a technical preview.

Tell us your boards, date range and intended uses. We will confirm scope and next steps for a preview or paid evaluation.

Request a technical preview