Why the API works this way
Google Docs exposes the document as a tree of structural elements. Paragraphs sit directly under the body and carry a HEADING attribute that maps to the ParagraphHeading enum: HEADING1 through HEADING6, plus NORMAL and TITLE. There is no built-in outline-numbering option, so every heading counter you see in the wild was either typed by hand or injected by a script.
Calling body.getParagraphs() returns a flat array of every paragraph in reading order, which makes the walk straightforward. The first time I tried this, I naively checked para.getHeading() and got undefined on every element — the correct call is para.getHeadingAttributes()[DocumentApp.Attribute.HEADING], which returns the enum constant you can compare against DocumentApp.ParagraphHeading.NORMAL.