I think content negotiation is great when your usecase supports it, like asking for XML or JSON. Also the mappings of content types should be well defined for all to see and edit. Rails is kinda a labyrinth in that regard.
I do tend to prefer actual file extensions though. Friendlier for humans (curl https://endpoint/item.json vs curl -H "accept: application/json" https://endpoint/item), and it is visible in logging infrastructure, sharable, etc.
Both file extensions and query parameters are functionally very similar.
What I don’t necessarily like about query params for this, is that their usage implies filtering. It’s a cultural assumption but it’s there.
Another point: you might actually have a literal file at a similar path that you serve, typically when you cache responses (managed cache or straight from your app server). Using file extensions gives you a bit more natural affordances. It’s just overal less clunky.
Something nice about accept headers is you can have your own formats, and include versioning information. That means I can have a consumer specify it can process V2 and V3 formats of the API, and when the API is upgraded or downgraded around a release everything works smoothly. Your API can serve both but it doesn't have to - it can be easier to have clients support two versions than support long running parallel API versions.
I sometimes prefer file extensions, but then consuming code can often get filled with appending or removing or changing formats.
Broadly, I like content negotiation as a concept because it describes what you actually want and is much more typed (as far as it can be). Adding file extensions feels very stringly typed processing.
Both have problems, you pick your poison and look wistfully at the greener grass on the other side.
Content negotiation is "friendlier to humans" in the sense that you can serve the right content to different clients using the same URL, transparent to the user. If the URL itself is never meant to be shared by the user, then I don't see the point.
Say, an RSS feed being served as a formatted and styled page to a browser, or a client that accepts the usual XML.
Your example of http://endpoint/item.json to me would imply that this is a static file- item.json -being served from the web server; whereas the explicit content-negotiated endpoint implies I'm hitting the endpoint item which will dynamically provide me a computed list of items.
I do tend to prefer actual file extensions though. Friendlier for humans (curl https://endpoint/item.json vs curl -H "accept: application/json" https://endpoint/item), and it is visible in logging infrastructure, sharable, etc.