Efficiently rendering sprites In order to naïvely render sprites, you can send single rectangles with their position, rotation, size, and texture. This is very easy, but given that reducing state changes, draw calls, and data uploads are good ways to improve rendering performance, it's clearly not an efficient solution.
Some techniques like passing each sprite as a single primitive and assembling a rectangle in the geometry shader are very appealing but aren't available if targeting a platform like WebGL or even just older hardware.

On top of issues like batching and texture aliases, how do you efficiently submit geometry to render sprites?

I'm interested in answers that are rendering API agnostic, but using a specific API like OpenGL or Direct X is fine for illustration purposes.
A good way to improve sprite rendering efficiency is geometry instancing. This lets you define a mesh using one vertex/index buffer pair, then render many instances of that mesh in a single draw call using a second vertex buffer whose elements define the instances. This is more widely supported than geometry shaders; in particular, it's is supported in WebGL via the ANGLE_instanced_arrays extension.

To apply this to sprites, one vertex buffer could define the shape of a sprite (i.e. a quad made of two triangles), then another vertex buffer could contain per-sprite information such as position, rotation, size, and so on.

If there are 4 vertices per sprite and N sprites, the vertex shader will be called a total of 4N times, onces for each vertex in each sprite. You'd use the vertex shader to put the information from the two buffers together, transforming each vertex appropriately to the sprite's properties. This allows the GPU to do the work of generating all the vertices for the sprites, and saves on data uploaded from the CPU.

Coincidentally, I published a small OpenGL instancing demo recently.

Using instancing this way does require that all the sprites are drawn with the same state, i.e. the same shader, textures, blending mode and so on. If different states are needed for some sprites, a separate batch of instances will be needed for each state combination.