Apple has published no specification of the Numbers format. What is described here comes from public reverse engineering: the iWorkFileFormat notes on the iWork 2013 format, the SheetJS project’s notes on how Numbers stores table cells, and the source and documentation of the numbers-parser library (see sources). It applies to Numbers 3 (2013) and later; earlier versions used a different, XML-based format that is not covered. Details can change with each Numbers release.
The package
- A document is a package (a folder that macOS shows as one file) or, as usually exchanged, a ZIP of the same files.
- The objects of the document are in
Index.zipin the bundle form, or anIndex/folder in the single-file form, as.iwafiles such asDocument.iwa,DocumentStylesheet.iwaandCalculationEngine.iwa.Index.zipis stored without compression and without ZIP64; iWork refuses to open one re-zipped with ordinary tools. Metadata/holdsBuildVersionHistory.plist,DocumentIdentifierandProperties.plist.Data/holds pictures, films and other media.- Preview images sit at the top level:
preview.jpg,preview-web.jpgandpreview-micro.jpg. - In a password-protected document nearly every file is encrypted with AES-128 and PKCS#7 padding.
Apple Pages and Keynote documents use the same container.
IWA compression
An .iwa file is a series of chunks in a variant of Google Snappy’s
framing format. Each chunk has a 4-byte header: a type byte, always 0 (compressed
data) in practice, then the length of the chunk data as a 24-bit little-endian number
(not counting the header). The data is a raw Snappy block. Unlike standard Snappy
framing there is no stream identifier chunk and no CRC-32C checksum. Joining the
decompressed chunks gives the object stream.
The object stream
The stream is a sequence of objects, each a varint giving the length of an
ArchiveInfo message, the ArchiveInfo itself (a Protocol
Buffers message), then the payload message or messages it describes.
| Message | Field | No. | Type | Meaning |
|---|---|---|---|---|
| ArchiveInfo | identifier | 1 | uint64 | The object’s id, unique within the document. |
message_infos | 2 | MessageInfo, repeated | One per payload message (in practice one). | |
| MessageInfo | type | 1 | uint32 | Number of the message type. |
version | 2 | uint32, packed | Version of the archive format, such as 1.0.5. | |
length | 3 | uint32 | Length of the payload message. | |
field_infos | 4 | FieldInfo, repeated | Not seen in files examined. | |
object_references | 5 | uint64, packed | Ids of other objects this one refers to. | |
data_references | 6 | uint64, packed | Ids of files in Data/ it uses. |
The type number says which Protocol Buffers message the payload is, but the table
from numbers to message names is not stored in the file: it lives in the application
(its TSPRegistry) and was recovered by inspecting the running program.
It differs slightly between Numbers, Pages and Keynote. Message definitions were
likewise extracted from the applications; their names carry a prefix for the part
of iWork they belong to, such as KN in Keynote and TST for
tables.
From the document to a table
Objects refer to each other by identifier through
TSP.Reference messages (field 1, identifier), which do not
say what type the target is. Object 1 is the document
(TN.DocumentArchive, type 1) and object 2 the package metadata
(TSP.PackageMetadata, type 11006), whose datas list maps
each file in Data/ to an identifier, a SHA-1 digest and a preferred file
name. The numbers of all message types are on the
message types page.
TN.DocumentArchive: field 1sheets, references to the sheets in order; field 8super, the sharedTSA.DocumentArchive; also the stylesheet, theme and calculation engine.TN.SheetArchive(type 2): field 1name; field 2drawable_infos, the tables, charts and other objects on the sheet.TST.TableInfoArchive(6000): the table as a drawable; field 2tableModelrefers to the model.TST.TableModelArchive(6001): field 3table_style, field 4base_data_store(an embeddedTST.DataStore), field 6number_of_rows, 7number_of_columns, 8table_name, plus header counts, default styles, merges, sorting and filters.
Table storage
A TST.DataStore holds:
| No. | Field | Holds |
|---|---|---|
| 1 | rowHeaders | HeaderStorage: bucketHashFunction and references to TST.HeaderStorageBucket objects (6006), whose headers give each row’s index, size (a float), hidingState, numberOfCells and optional cell and text styles. |
| 2 | columnHeaders | Reference to one bucket for the columns, in the same form (widths). |
| 3 | tiles | TileStorage: a list of tile ids with references to TST.Tile objects (6002), and the tile size (rows per tile; 256 by default). |
| 4 | stringTable | Data list of plain strings. |
| 5 | styleTable | Data list of cell and text styles. |
| 6 | formula_table | Data list of formulas. |
| 9 | rowTileTree | Maps rows to tiles. |
| 11 | format_table_pre_bnc | Formats, older layout. |
| 17 | rich_text_table | Data list of rich-text payloads (optional). |
| 21 | control_cell_spec_table | Data list of checkbox, slider, stepper, rating and pop-up settings (optional). |
| 22 | format_table | Data list of number, date and other formats (optional). |
Each list is a TST.TableDataList (6005 or 6201): listType
(1 string, 2 format, 3 formula, 4 style, 5 formula error, 8 rich-text payload, 12
control cell spec), nextListID, and entries each with a
key, a refcount and one of string (3), a
reference (4), a formula (5, a
TSCE.FormulaArchive holding the formula as a tree of nodes), a
format (6), a rich_text_payload reference (9, to a
TST.RichTextPayloadArchive, 6218, whose storage is a
TSWP.StorageArchive) or a cell_spec (12). A cell stores keys
into these lists of its own table, not document-wide indexes.
Tiles and rows
A tile holds rowInfos, one TST.TileRowInfo per row that has
cells; the absolute row number is the tile id × the tile size + the row’s
tile_row_index. Numbers parsers check the tile’s
last_saved_in_BNC flag: tiles not saved in the current (“BNC”) form use
the older _pre_bnc buffers, which are not covered here.
| No. | Field | Meaning |
|---|---|---|
| 1 | tile_row_index | Row within the tile. |
| 2 | cell_count | Number of cells stored. |
| 3, 4 | cell_storage_buffer_pre_bnc, cell_offsets_pre_bnc | Older storage. |
| 5 | storage_version | 5 for the current cell records. |
| 6 | cell_storage_buffer | The row’s cell records, back to back. |
| 7 | cell_offsets | One signed 16-bit little-endian offset per column into the storage buffer; −1 (FF FF) means no cell in that column. |
| 8 | has_wide_offsets | When true, multiply each offset by 4. |
A cell’s record runs from its offset to the next non-negative offset, or to the end of the buffer.
Cell records (storage version 5)
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 1 | version | Always 5 in current files. |
1 | 1 | cell type | Cell type. |
2 | 4 | Not decoded by the public sources. | |
6 | 2 | format hints | Which format ids are present, used when the cell is formatted “Automatic” (below). |
8 | 4 | field mask | Which fields follow (below), little-endian. |
12 | varies | fields | The fields whose mask bits are set, in increasing bit order. |
| Mask bit | Size | Field |
|---|---|---|
0x000001 | 16 | Number as a decimal128 (below), for number and currency cells. |
0x000002 | 8 | IEEE double: a Boolean (true when greater than 0) or a duration in seconds. |
0x000004 | 8 | IEEE double: a date, as seconds since 2001-01-01 00:00. |
0x000008 | 4 | String key in the string list. |
0x000010 | 4 | Rich-text key. |
0x000020 | 4 | Cell style key. |
0x000040 | 4 | Text style key. |
0x000080 | 4 | Conditional style key. |
0x000100 | 4 | Applied conditional-style rule. |
0x000200 | 4 | Formula key. |
0x000400 | 4 | Control cell spec key (checkbox, rating, slider, stepper, pop-up). |
0x000800 | 4 | Formula error key. |
0x001000 | 4 | Suggested format kind. |
0x002000 | 4 | Number format key. |
0x004000 | 4 | Currency format key. |
0x008000 | 4 | Date format key. |
0x010000 | 4 | Duration format key. |
0x020000 | 4 | Text format key. |
0x040000 | 4 | Boolean format key. |
0x080000 | 4 | Comment storage key. |
0x100000 | 4 | Import warning set key. |
Keys are signed 32-bit little-endian integers. The format-hint word at offset 6 has
bit 0x0001 number format present, 0x0002 currency,
0x0004 duration, 0x0008 date, 0x0020 Boolean,
0x0080 text, 0x0800 a currency-related hint and
0x8000 a formula-related hint, as numbers-parser’s documentation
describes them; the library itself only copies the word unchanged.
Cell types
| Byte 1 | Name in the schema | Value |
|---|---|---|
| 0 | genericCellType | Empty: a 12-byte record with no value. |
| 1 | spanCellType | Part of a span; not a value. |
| 2 | numberCellType | Number, in the decimal128 field. |
| 3 | textCellType | Text, by string key. |
| 4 | formulaCellType | Formula (in practice a formula cell carries the type of its result, plus a formula key). |
| 5 | dateCellType | Date and time, in the date field. |
| 6 | boolCellType | Boolean, in the double field. |
| 7 | durationCellType | Duration in seconds, in the double field. |
| 8 | formulaErrorCellType | Formula error, by error key. |
| 9 | automaticCellType | Rich text, by rich-text key. |
| 10 | (none) | Currency, in the decimal128 field; handled by numbers-parser but not in the schema’s enum. |
The schema has a separate CellValueType enum used by
TST.Cell messages in command and pasteboard archives, not by tile records:
0 empty, 1 number, 2 string, 3 provided, 4 date, 5 Boolean, 6 duration, 7 error, 8
rich text, 9 currency.
Numbers: the decimal128 field
Numbers are stored in decimal, not binary floating point. In the 16 bytes: bit 7
of byte 15 is the sign; bits 0–6 of byte 15 (high) and bits 1–7 of byte 14 (low)
form a 14-bit exponent with a bias of 0x1820 (6176); bit 0 of byte 14
and bytes 13 down to 0 form the coefficient, most significant first. The value is
coefficient × 10exponent − 6176, negated if the sign bit is set.
Dates count seconds from 1 January 2001 00:00; durations are plain seconds.
Styles
A cell style key leads, through the style list, to a
TST.CellStyleArchive (6004), whose super is a
TSS.StyleArchive (name, style_identifier,
parent, stylesheet) and whose field 11
cell_properties holds the fill (1, cell_fill), text wrap
(3) and the top, right, bottom and left strokes (10–13). Unset properties come from
the parent style. Text styles are TSWP paragraph and character styles. The table
model names default text styles for the body, header rows, header columns and footer
rows.
Password-protected documents
A protected document has a .iwpv2 verifier and a .iwph
hint. The verifier is 104 bytes: version (2 bytes, 2), format version (2, 1),
iteration count (4), salt (16), IV (16) and a 64-byte encrypted block. The key is 16
bytes from PBKDF2-HMAC-SHA1 of the password with that salt and count; decrypting the
block with AES-128-CBC must give 64 bytes whose last 32 are the SHA-256 of the first
32. Each encrypted IWA file is a 16-byte IV, the AES-128-CBC ciphertext and 20
trailing bytes that are dropped; after decryption and removal of the PKCS#7 padding,
the first 16 bytes are discarded and the rest is the IWA stream.
Sources
- numbers-parser – open-source reader and writer; its file-format document (
docs/api/file-format.rst, which says it is maintained with AI help and should be checked),cell.py,model.py,constants.pyandsrc/protos/TSTArchives.proto, read on 12 November 2026; the cell layout above was checked against the code - iWorkFileFormat – Sean Patrick O’Brien’s description of the iWork 2013 container, Snappy framing and object stream
- SheetJS IWA notes – the SheetJS project’s notes on IWA and Numbers cell storage