I have a serverless pipeline on Google Cloud. It consists of three total steps:
- A video uploader that sends videos to Google Cloud Storage. Working fine.
- An eventarc + pub/sub trigger that fires whenever a video is uploaded to GCS to a cloud run application called 'post processor'.
- The post processor extracts some information regarding this file and sends a API request to my Django API, to create a history f this recording to my database.
The post processor is implemented using functions_framework.cloud_event. I also created some logs so I could debug it in Google Cloud Logging. Here's one example as follows:
2025-09-24 14:53:05,135 INFO main: RECEIVED DATA FROM EVENTARC (POST-PROCESSING): Original Filename=7614_2025_09_23_16_46_17.mp4, Serial=7614, UTC=2025-09-23T16:46:17+00:00, Duration=0s, Frames=0 It received all the information correctly... Generated a payload from it and posted a request to my Django API. Payload sent is this:
{ "raw_file": null, "raw_file_gcp": "https://storage.cloud.google.com/homolog-videos/7614_2025_09_23_16_46_17.mp4", "motion_file": null, "event_at": "2025-09-23T16:46:17+00:00", "camera": 7614, "frames_count": 0, "duration_seconds": 0, "event_end_at": "2025-09-23T16:46:17+00:00", "size_mb": 0 } However, when seeing the request on my Django API, it receives no payload at all and the requests comes as a GET method instead of the POST method. For example, the request above arrived like this: [GET] /api/recordings/ | From: 34.34.234.34 | Payload: {}
This unintentionally triggers a list endpoint instead of creating a record....
I reproduced my production settings locally, (DEBUG=False, ENVIRONMENT="production", etc) and kept the same Post Processor code and library versions. I used NGrok to process videos sent to GCS, instead of CloudRun, and to my surprise, locally, when outside of Google Cloud Services (Cloud Run) the API correctly received the POST request with the correct JSON... Same exact code as in cloud run, library versions, etc... Behaved as expected with no conversion to GET.
The libraries i'm using are as follows:
functions-framework==3.5.0 cloudevents==1.11.0 cachetools==6.1.0 pytz==2025.1 requests==2.32.5 It's a small script that only performs this post to the API.
What i'm not getting is what could be causing requests.post made from Cloud Run to result in a GET with a empty body conversion in my Django REST Framework...? Is there a setting in my ingress.yaml setting I should do? A rule, host definition, etc, there that could be causing this issue?
Edit 1
Below is a minimal code example showing how the post processor works, that is activated whenever a GCS upload is finished.
import functions_framework from cloudevents.http import CloudEvent # Triggered by a Cloud Storage "object finalized" event @functions_framework.cloud_event def hello_gcs(cloud_event: CloudEvent): data = cloud_event.data name = data["name"] # e.g. "7614_2025_09_23_16_46_17.mp4" # ...parse metadata and compute (serial, date, duration, frames, filename)... serial, date, duration, frames, filename = process_video(name) create_recording(serial, date, duration, frames, filename) return "OK" Where the payload is built and the API call is made:
def create_recording(serial_cam, date, duration, frames, filename): API_URL = os.environ.get("API_URL") API_TOKEN = os.environ.get("API_TOKEN") api = WrapperAPI(API_URL, API_TOKEN) # Build payload payload = { "raw_file": None, "raw_file_gcp": f"https://storage.cloud.google.com/{BUCKET}/{filename}", # ... etc ... A normal payload, nothing fancy. } # Perform the POST recording = api.create(resource="recordings", json=payload, hide_error_msg=False) API wrapper (uses requests.post, not GET.):
import requests class WrapperAPI: def __init__(self, base_url, token): self.url = base_url.rstrip("/") self.headers = {"Authorization": token, "Content-Type": "application/json"} def create(self, resource, json, hide_error_msg=False): try: url = f"{self.url}/{resource}/" resp = requests.post(url, json=json, headers=self.headers) # <-- POST resp.raise_for_status() return resp.json() # ... etc ... Exception treatment... No magic here. Just the normal exception treatment you would expect. Edit 2
We did some investigation and found some similar threads that have some similar problems. Tho they were not in GKE contexts... When Python’s requests client followed it, it converted the method to GET and dropped the body—explaining exactly why the backend saw a GET with empty payload.
- Python Requests library post interpreted by Django as GET for some reason
- Python Requests POST doing a GET?
The question was exact 'Python Requests library post interpreted by Django as GET for some reason' and we had exactly the same problem. But we only found this after @shiro answer. We actually found it because of it. I wanted to link it here in case anyone comes across this.
POSTand GKE is receiving aGET. Absent evidence, the conclusion is that you're making aGET(and receiving aGET). Please include a minimal repro including the Cloud Run source code and details of how the GKE service is exposed.create_recordingdirectly) against a debug server locally, containerized locally and deploy to a local Kubernetes cluster and every time the server receivesPOST'sPOST's