I went down the rabbit hole with this, so let me explain.
You are ofc right in that a conversion to JSON is not I/O bound, and it's hardly CPU-bound either (unless the response body is really big).
However, in the example of aiohttp, it's not the conversion to JSON that is being awaited, although it misleadingly appears so in the provided interface.
What aiohttp does is, even though the server returns everything (the status code, headers and body) in one go (assuming a non-streaming situation, that's just how HTTP works), the client can buffer all of it into memory, and read it in steps. So, it can read just the status code first, and depending on its value, it can proceed or not with the other steps. Each step is, technically speaking, a separate I/O operation, even though we generally don't consider reading from memory as a bottleneck, since it's super fast.
Also, if the response body is huge, it may have not fully arrived yet, so the extra step would involve network I/O too.
This separation is quite useful for large responses, but mostly just adds asyncio overhead otherwise. Remember that async programming should be used only where it makes sense, if not every function would be an awaitable by default and we wouldn't have async/await keywords.
It's a baked in decision in aiohttp though, so there's not much that you can do. Ultimately, you gain more than what you lose here overall (compared to using requests throughout your project) so you should probably be fine with the trade-off.
session.get()(an asynchronous call) will have completed before you get toresp.json()?