Creating mixed content with nested bullet points in Google Docs using Python API

Background

I have a Python project where I need to insert different types of content into a Google Doc. I already have my service account set up and the document ID ready to use.

What I’m trying to do

I want to take a list that has regular text mixed with different levels of bullet points and put it into a Google Doc. Here’s what my data looks like:

content_array = [
    "Regular paragraph text goes here first.",
    "• Top level bullet point one",
    "• Top level bullet point two", 
    "  • Second level item A",
    "    • Third level sub-item i",
    "    • Third level sub-item ii",
    "  • Second level item B",
    "Another regular paragraph in between.",
    "• One more top level bullet",
]

I want this to show up in the document as normal paragraphs mixed with properly formatted numbered lists that have the right indentation levels.

My current approach

I tried processing the list by detecting the bullet markers and converting them:

processed_text = ""
for line in content_array:
    if line.startswith('• '):
        processed_text += line[2:] + '\n'
    elif line.startswith('  • '):
        processed_text += '\t' + line[4:] + '\n'
    elif line.startswith('    • '):
        processed_text += '\t\t' + line[6:] + '\n'
    else:
        regular_text = line + '\n'
        processed_text += regular_text
        end_pos = start_pos + len(processed_text) + 1
        start_pos_regular = end_pos - len(regular_text)

final_end = start_pos + len(processed_text) + 1

update_requests = [
    {
        "insertText": {
            "text": processed_text,
            'location': {'index': start_pos},
        }
    },
    {
        "createParagraphBullets": {
            'range': {
                'startIndex': start_pos,
                'endIndex': final_end,
            },
            "bulletPreset": "NUMBERED_DECIMAL_ALPHA_ROMAN",
        }
    },
    {
        "deleteParagraphBullets": {
            'range': {
                'startIndex': start_pos_regular,
                'endIndex': end_pos,
            },
        }
    },
]

try:
    docs_service.documents().batchUpdate(documentId=document_id, body={'requests': update_requests}).execute()
except HttpError as err:
    print(f"Error occurred: {err}")

The problem is that this applies bullet formatting to everything, including the regular paragraphs. I need a way to only apply the list formatting to the actual list items while keeping the normal text as regular paragraphs. How can I fix this?

Yeah, you’re mixing everything together first, then trying to separate it. Insert each line individually instead and only track ranges for bullet items. When you hit a bullet line, note where it starts and ends. After all insertions, run createParagraphBullets just on those ranges. Skip regular paragraphs completely - way cleaner.

I hit this same issue a few months back when building a document generator for project reports. Your problem is you’re treating the whole content block as one operation instead of handling each paragraph type separately. What worked for me was processing each item in the content array one by one and tracking index positions more carefully. Don’t concatenate the text upfront - insert each paragraph as you go and only apply formatting to bullet points after insertion. Regular paragraphs should go in without any list formatting.

Index positions get messy since every insertion shifts things around. I found it way easier to work backwards from the end of the document or use separate batchUpdate calls for each content group. This avoids the headache of index calculations and lets you apply createParagraphBullets only to ranges that actually have bullet content. Also, try updateParagraphStyle for better control over indentation levels instead of using tabs in preprocessing.

Your range calculations are messed up because you’re mixing content types in one operation. I hit this same issue automating reports last year. Don’t preprocess everything into one string - build separate request batches for different content types instead. Insert regular paragraphs first, then handle bullet sections as separate groups. For each bullet group, insert all the text together, then run createParagraphBullets on just that range. Track start and end positions for each content block instead of trying to exclude ranges afterward. Your deleteParagraphBullets approach is backwards - just apply formatting where you want it rather than applying it everywhere then removing it.