ffmpeg -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos" -c:v pam -f image2pipe - | convert -delay 10 - -loop 0 -layers optimize output.gif
ffmpeg options:
-vf "fps=10,scale=320:-1:flags=lanczos" a filtergraph using the fps and scale filters. fps sets frame rate to 10, and scale sets the size to 320 pixels wide and height is automatically determined and uses a value that preserves the aspect ratio. The lanczos scaling algorithm is used in this example.
-c:v pam Chooses the pam image encoder. The example outputs the PAM (Portable AnyMap) image format which is a simple, lossless RGB format that supports transparency (alpha) and is supported by convert. It is faster to encode than PNG.
-f image2pipe chooses the image2pipe muxer because when outputting to a pipe ffmpeg needs to be told which muxer to use.
convert options:
-delay See Setting frame rate section below.
-loop 0 makes infinite loop.
-layers optimize Will enable the general purpose GIF optimizer. See ImageMagick Animation Optimization for more details. It is not guaranteed that it will produce a smaller output, so it is worth trying without -layers optimize and comparing results.
Setting frame rate
Set frame rate with a combination of the fps filter in ffmpeg and -delay in convert. This can get complicated because convert just gets a raw stream of images so no fps is preserved. Secondly, the -delay value in convert is in ticks (there are 100 ticks per second), not in frames per second. For example, with fps=12.5 = 100/12.5 = 8 = -delay 8.
convert rounds the -delay value to a whole number, so 8.4 results in 8 and 8.5 results in 9. This effectively means that only some frame rates are supported when setting a uniform delay over all frames (a specific delay can be set per frame but that is beyond this answer).
-delay appears to be ignored if used as an output option, so it has to be used before - as shown in the example.
Lastly, browsers and image viewers may implement a minimum delay, so your -delay may get ignored anyway.