Apps Script: get Docs Heading Id

As you may have noticed, the heading id isn’t one of the attributes, and there isn’t a method to retrieve it in the basic Document Service; however, there are a couple of workarounds.

Heading Id Argument from Table of Contents

You can get the heading argument from the Table of Contents (TOC), and that may be the better way to go depending on your use case:

  • It’s about twice as fast for large documents as the alternative described below; it can still be fairly slow from a user experience perspective.
  • It depends on your users updating the TOC, which puts it a bit outside your script’s scope of control since there isn’t an API to update the TOC.
  • It depends on headings (ie. the text) being unique; this works well for meeting minutes documents with dated headings. If the headings aren’t unique, the script will return the heading id argument of the first matching heading, so either use reverse chronological order in your documents or invert the loop iteration orders (eg. for (var i = numBodyChildren; i — ;)).

Note that this returns the full heading argument, ie. “#heading=...”.

Heading Id from Advanced Docs Service

You can get the heading id from the Advanced Docs Service if you can’t rely on your users updating the TOC or providing unique headings; however, there are other constraints.

  • It takes about twice as long for large documents (eg. 30+ seconds for 150 page document) as using the basic Table of Contents approach; this is mostly the time required to open a large document with the Advanced Service.
  • Your users need to enter the document headings themselves because headings generated by Apps Script (either basic or Advanced service) don’t receive heading Ids until the TOC is refreshed, so you’d be back where you started. So this approach doesn’t help for automatically generated document sections.

This approach doesn’t depend on unique heading text; only the name of the named range containing the heading needs to be unique, and your script can control this.

Create a named range using the basic service, and retrieve the heading Id in the matching named range in the Advanced Service. Unfortunately the named range Ids don’t match between the basic and Advanced Service, so you need to rely on the name of the ranges; just prune the named ranges before you create a new one, and generate unique names based on your script id, and perhaps a date/time stamp, and you should be good.



These are my personal writings; the views expressed in these pages are mine alone and not those of my employer, Google.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store