I m trying to pull the full list of users having access to GA4 but can only seems to get a subset of the full users list.
What I’ve Tried:
I’m using the GET /{parent=properties/*}/accessBindings endpoint to fetch users. This API requires pagination since it only returns a maximum of 50 users per request and provides a nextPageToken if more data is available.
Here’s the logic I’ve implemented so far:
If a nextPageToken is present, use it to fetch the next batch of results. Repeat until no more nextPageToken is provided.
So far, this logic works fine for properties/accounts with a small number of users. However, for a property with more than 400 users, I can only retrieve around 80 users total, despite the fact that the Admin Console clearly shows hundreds of users.
What’s Happening: First Request: Returns 50 users and includes a nextPageToken.
Second Request: Fetches 30 more users but does not provide a nextPageToken, so the loop stops.
Final Result: Only 80 users are retrieved, even though I know there are many more users in the property.
What I’ve Checked:
Pagination Logic: Works correctly for other cases, so I don’t think it’s an issue in my code.
API Quotas: I’m not hitting any rate limits.
Access Token Scopes: Using .manage.users access.
My token seems fine since the first two requests work.
Questions: Has anyone successfully retrieved a complete list of users from the Google Analytics Admin API? If so, how did you handle this situation?
Is there some hidden limitation or filtering happening in the API that’s not mentioned in the documentation?
Are there any known bugs with nextPageToken in the accessBindings endpoint that could be causing this?
Additional Info: The property has more than 400 users.
I’m fetching the accessLevel, emailAddress, and other details for each user.
import requests
import pandas as pd
def list_all_users(access_token, resource_id):
base_url = f"/{resource_id}/accessBindings"
all_bindings = [] # List to store all user-role bindings
next_page_token = None
while True:
# Construct URL with pagination token if available
url = base_url
if next_page_token:
url += f"?pageToken={next_page_token}"
print(f"Fetching URL: {url}") # Debugging info
# Make the GET request
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
# Handle errors
if response.status_code != 200:
raise Exception(f"Error fetching access bindings: {response.text}")
# Parse response JSON
content = response.json()
# Add access bindings to the list
if "accessBindings" in content:
all_bindings.extend(content["accessBindings"])
# Check for nextPageToken
next_page_token = content.get("nextPageToken")
if not next_page_token:
print("No nextPageToken, stopping.") # Debugging info
break
# Convert list of bindings to a DataFrame
if all_bindings:
df = pd.DataFrame(all_bindings)
else:
df = pd.DataFrame() # Empty DataFrame if no bindings
print(f"Total users fetched: {len(df)}") # Debugging info
return df
if __name__ == "__main__":
# Replace these values with your actual credentials and resource ID
ACCESS_TOKEN = # OAuth 2.0 token
RESOURCE_ID = # Replace with your account or property ID
# Fetch all users
users_df = list_all_users(ACCESS_TOKEN, RESOURCE_ID)