I'm working on registering pageviews to track API usage right now, here's what I've found:
XTOTHEL is right about setting the content type to content/json above. In addition to specifying the content type you also have to send JSON data as the CURLOPT_POSTFIELDS data.
Also per their specification the api_secret and measurement_id need to be part of the URI: https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#required_parameters
Lastly, you can use debug mode to validate your responses and figure out what's going on now by simply changing the URL to google-analytics.com/debug/mp/collect
Here's the code I'm working with right now:
//retrieve or generate GA tracking id
            if (empty($_COOKIE['_cid'])) {
                setcookie('_cid', vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4)));
            }
            $data = '{"client_id":"'.$_COOKIE['_cid'].'","events":[{"name":"load_endpoint","params":{"page_location":"'.$request->fullUrl().'"}}]}';
            echo '<pre>';
            print_r($data);
            $measurement_id = 'G-xxxxx';
            $api_secret = 'xxxx';
            $url = 'https://www.google-analytics.com/debug/mp/collect?api_secret='.$api_secret.'&measurement_id='.$measurement_id;
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            curl_close($ch);
            echo $response;
This works to a certain extent. Currently it's registering the page view as a custom event instead of an actual pageview though. I'm still trying to figure out how to get them to come through as page views.
Follow up
After a little more debugging I figured out page views are actually working, they just weren't showing up in some of the views. The fix for that was to add page_title into the params:
$data = '
{
  "client_id": "'.$_COOKIE['_cid'].'",
  "events": [
    {
      "name": "page_view",
      "params": {
        "page_location": "'.$request->fullUrl().'",
        "page_title": "'.$request->path().'"
      }
    }
  ]
}
';
A few extra notes for whoever comes next:
- Debug mode did return some useful validation errors for invalid top-level parameters (client_id, events) - but it didn't return errors for anything inside of the "params" for events. IE - i put "page_asdtitle" instead of "page_title" and it accepted it just fine.
 
- None of the tests I sent through actually showed up in the debug panel while using debug mode. I suspect this is because of the data propagation delay, it's probably not loading realtime data.
 
- Using a JSON validator can help. Make sure you use objects and arrays where GA tells you to.
 
- If you get stuck figuring out why your PHP code doesn't work, write the code as a browser event in JavaScript and run it in your browser. There's tons of examples on how to do that. From there, you can use Dev Tools -> Network to inspect the request. If you right click on the google analytics request to the 'collect' endpoint you'll see an option to Copy Request as CURL. Put that into a text editor and compare it to what your PHP code is sending.
 
- To ACTUALLY test this without the massive propagation delay you can login to Google Analytics, go to Reports -> Realtime, and you should see your data show up within 30-60 seconds if it's working. Realtime data will NOT show up if you're using the /debug/ endpoint though.