Managing Monday.com Items and Subitems Using Python Scripts for Monday.com Updates

Automating Monday.com workflows often requires more than basic triggers and actions. By using Python snippets within Zapier (or similar automation platforms), you can perform advanced tasks like generating search permutations, querying subitems, and conditionally updating column values all in a single step.

This guide walks you through a practical example: taking a raw property address, finding the matching item in Monday.com, verifying its subitems, and updating a status column if specific conditions are met.

While this example uses specific real estate terms (like "MLS" and "Property Address"), the core concepts—searching by permutations, reading subitems, and executing inline GraphQL mutations—can be adapted to any industry or workflow.

How the workflow operates

Before diving into the code, it helps to understand the logic flow. This script takes a messy input, cleans it up, searches Monday.com, and makes decisions based on the results.

flowchart TD
    A["Raw Address Input"] --> B["Generate Permutations (St vs Street)"]
    B --> C["Search Monday.com API"]
    C --> D{"Item Found?"}
    D -- "No" --> E["Return 'No Match'"]
    D -- "Yes" --> F["Check Subitems & Stage"]
    F --> G{"Is Measurement Job?"}
    G -- "Yes, but parent says No" --> H["Mutate: Update Parent to 'Yes'"]
    G -- "Already Yes / Not a job" --> I["Skip Update"]
    H --> J["Return Item Data to Zapier"]
    I --> J

Implementing the script in Zapier

To use this script, you will use the Code by Zapier app and select the Run Python action.

  1. 1

    Configure your input data

    In your Zapier step, map the data you want to pass into the Python script. For this script, you will need:

    • api_token: Your Monday.com API Token.

    • raw_address: The address you want to search for.

    • last_modifying_user_name: (Optional) The name of the user triggering the action.

  2. 2

    Customize your configuration variables

    At the top of the script, you must replace the placeholder IDs with your actual Monday.com Board ID and Column IDs. You can find Column IDs by enabling "Developer Mode" in your Monday.com profile settings.

  3. 3

    Add the Python code

    Copy and paste the following script into the Code field in Zapier.

The Python Script

import requests
import json
import re

# ==========================================
# 1. CONFIGURATION
# ==========================================
API_TOKEN = input_data.get('api_token')
BOARD_ID = "7932413985" # Replace with your Board ID
PROPERTY_ADDR_COL = "text0__1" 
STAGE_COL = "deal_stage"
MEASUREMENT_COL = "color_mkv9zkkc" 
DELIVERY_COL = "status_mkkac7g6" 
PARENT_FOLDER_COL = "text_mktj9wa4"
SUB_ITEM_QBO_COL = "status_mkkaxp21" 
SPLIT_STATUS_COL = "color_mm388khb"  

HEADERS = {
    "Authorization": API_TOKEN, 
    "Content-Type": "application/json", 
    "API-Version": "2023-10"
}

# Format user email
raw_modifier = input_data.get('last_modifying_user_name', '').strip()
modifier_email = f"{raw_modifier.split()[0].lower()}@yourdomain.com" if raw_modifier else ""

# ==========================================
# 2. STANDARDIZE & PERMUTATE ADDRESS
# ==========================================
raw_addr = input_data.get('raw_address', '').strip()
clean_addr = re.sub(r'[.]', '', raw_addr).lower() 

directions = {"n": "north", "s": "south", "e": "east", "w": "west", "ne": "northeast", "nw": "northwest", "se": "southeast", "sw": "southwest"}
suffixes = {"st": "street", "rd": "road", "ave": "avenue", "dr": "drive", "ln": "lane", "ct": "court", "pl": "place", "blvd": "boulevard", "hwy": "highway"}

def get_variations(addr):
    vars = {addr}
    # Generate direction variations
    for abbr, full in directions.items():
        if re.search(rf'\b{abbr}\b', addr): vars.add(re.sub(rf'\b{abbr}\b', full, addr))
        if re.search(rf'\b{full}\b', addr): vars.add(re.sub(rf'\b{full}\b', abbr, addr))
            
    final_set = set()
    # Generate suffix variations
    for v in vars:
        final_set.add(v)
        for abbr, full in suffixes.items():
            if re.search(rf'\b{abbr}\b', v): final_set.add(re.sub(rf'\b{abbr}\b', full, v))
            if re.search(rf'\b{full}\b', v): final_set.add(re.sub(rf'\b{full}\b', abbr, v))
    return [v.title() for v in final_set]

search_values = get_variations(clean_addr)

# ==========================================
# 3. TARGETED SEARCH
# ==========================================
search_query = """
query ($boardId: ID!, $colId: String!, $values: [String]!) {
  items_page_by_column_values (limit: 50, board_id: $boardId, columns: [{column_id: $colId, column_values: $values}]) {
    items {
      id
      name
      group { title }
      column_values { id text }
    }
  }
}
"""

res = requests.post("https://api.monday.com/v2", 
                    json={'query': search_query, 'variables': {
                        'boardId': BOARD_ID, 
                        'colId': PROPERTY_ADDR_COL, 
                        'values': search_values
                    }}, headers=HEADERS).json()

found_items = res.get('data', {}).get('items_page_by_column_values', {}).get('items', [])

# ==========================================
# 4. VERIFICATION & MUTATION LOOP
# ==========================================
target_id = None
has_mls_subitem = False
current_stage = "No Match"
delivery_status = "" 
parent_folder_id = "" 
split_status = "" 
debug_log = [f"Searched for: {search_values}"]

if found_items:
    for item in found_items:
        cv_map = {cv['id']: (cv.get('text') or "").strip() for cv in item.get('column_values', [])}
        item_stage = cv_map.get(STAGE_COL, "")
        measurement_val = cv_map.get(MEASUREMENT_COL, "")
        group_title = item.get('group', {}).get('title', "")
        
        # Phase 1: Basic Group & Stage parameters
        if item_stage != "SDC" and group_title == "Active Deals":
            
            # Query sub-items
            sub_query = "query ($ids: [ID!]) { items (ids: $ids) { subitems { name column_values { id text } } } }"
            sub_res = requests.post("https://api.monday.com/v2", json={'query': sub_query, 'variables': {"ids": [item['id']]}}, headers=HEADERS).json()
            subitems = sub_res.get('data', {}).get('items', [{}])[0].get('subitems', [])
            
            is_measurement_by_subitem = False
            item_has_mls = False
            
            for sub in subitems:
                if "MLS Data Collection" in sub.get('name', ''):
                    item_has_mls = True
                
                sub_cv_map = {cv['id']: (cv.get('text') or "").strip() for cv in sub.get('column_values', [])}
                if "measurements" in sub_cv_map.get(SUB_ITEM_QBO_COL, "").lower():
                    is_measurement_by_subitem = True
            
            # Phase 2: Accept if parent says Yes OR if sub-items prove it
            if measurement_val.lower() == "yes" or is_measurement_by_subitem:
                target_id = item['id']
                current_stage = item_stage
                delivery_status = cv_map.get(DELIVERY_COL, "") 
                parent_folder_id = cv_map.get(PARENT_FOLDER_COL, "") 
                split_status = cv_map.get(SPLIT_STATUS_COL, "")
                has_mls_subitem = item_has_mls
                
                # INLINE MUTATION: Auto-flip parent column if needed
                if measurement_val.lower() != "yes" and is_measurement_by_subitem:
                    update_query = """
                    mutation ($boardId: ID!, $itemId: ID!, $colId: String!, $value: JSON!) {
                      change_column_value (board_id: $boardId, item_id: $itemId, column_id: $colId, value: $value) { id }
                    }
                    """
                    update_vars = {
                        "boardId": BOARD_ID,
                        "itemId": target_id,
                        "colId": MEASUREMENT_COL,
                        "value": json.dumps({"label": "Yes"})
                    }
                    try:
                        up_res = requests.post("https://api.monday.com/v2", json={'query': update_query, 'variables': update_vars}, headers=HEADERS).json()
                        debug_log.append(f"Flipped parent item {target_id} to Yes.")
                    except Exception as e:
                        debug_log.append(f"Failed to auto-flip parent column: {str(e)}")
                break

return {
    "target_id": target_id,
    "match_found": target_id is not None,
    "has_mls_subitem": has_mls_subitem,
    "current_stage": current_stage,
    "delivery_status": delivery_status, 
    "parent_folder_id": parent_folder_id, 
    "split_status": split_status, 
    "modifier_email": modifier_email, 
    "debug_info": debug_log
}

Never hardcode your API Token directly into the Python script. Always pass it securely via Zapier's input_data to prevent accidental exposure if you share or export your Zap.

Understanding the GraphQL Queries

If you're new to Monday.com's API, the script uses two distinct types of GraphQL operations.

Querying Items by Column Value

The script uses items_page_by_column_values to find items matching the generated address permutations. This is much more efficient than pulling all items on a board and filtering them in Python.

Notice that the query specifically requests the group { title } and column_values { id text } so the script has enough context to verify the item locally.

Mutating (Updating) a Column Value

When the script discovers that a subitem indicates a "measurement" job, but the parent item doesn't reflect this, it executes a change_column_value mutation.

Because Monday.com requires strict JSON formatting for column updates, the Python script uses json.dumps({"label": "Yes"}) and passes it to the GraphQL $value variable typed as JSON!.

Expected Output Variables

Once the script finishes running, it returns a dictionary of values. You can map these variables into subsequent steps in your Zap (for example, sending a Slack message or updating a CRM).

Output VariableDescription
target_idThe Monday.com Item ID if a match was successfully found and verified.
match_foundA boolean (True/False) indicating if the search was successful.
has_mls_subitemBoolean indicating if an "MLS Data Collection" subitem exists.
current_stageThe value of the deal stage column for the matched item.
split_statusThe extracted split status value (e.g., "Parent").
debug_infoA list of log messages detailing search parameters and mutation results, useful for troubleshooting in Zapier's history.

Monday.com API Documentation

Want to write your own custom queries? Check out the official Monday.com GraphQL API reference for more details on available objects and mutations.