If you call the API with the image encoded as a URL parameter there will be image size limitations. The solution is to instead post multipart/form-data to the API:
With[{cloudobject = FileNameJoin[{$CloudRootDirectory, "ImageEffectAPI"}]},
CloudDeploy[
APIFunction[{"x" -> "Image"},
ImageEffect[#x, "Charcoal"] & ],
cloudobject, Permissions -> "Public" ];
ToExpression[
URLFetch[cloudobject,
"Method" -> "POST",
"MultipartElements" ->
{{"x", "image/jpeg"} ->
ExportString[ExampleData[{"TestImage", "Lena"}], "JPEG"] } ] ] ]
The above solution deploys exactly the same API that the question proposes and returns a byte RawArray image string. The final image is generated by applying ToExpression to the RawArray string. It is perhaps preferable to deploy a slightly modified API, which returns a Base64 encoded image instead of a RawArray string:
APIFunction[{"x" -> "Image"},
ExportString[
ImageEffect[#x, "Charcoal"], {"Base64", "JPEG"}] & ]
Deploy and call this API exactly as above, but instead of ToExpression apply ImportString to the result:
ImportString[URLFetch[cloudobject, ... ], {"Base64", "JPEG"}]
Comment:
The documentation that an APIFunction can accept multipart/form-data is to say the least a bit tenuous: The APIFunction documentation mentions in the last Details and Options bullet that this function “allows the same options as FormFunction” and under Properties & Relations that the “APIFunction is designed to be interchangeable with FormFunction.” A FormFunction example in the Scope section gives an image file picker. From this one might guess that a FormFunction can accept multipart/form-data and that an APIFunction also can. After intercepting some FormFunction HTTP traffic and a bit of experimentation with URLFetch, the solution presented in this answer should in retrospect seem obvious.