Astrometry.net solvers should return the jobid
Currently, both solvers call `monitor_submission` before returning. This has for consequence that the only way to obtain the jobid, which is required to download the result files from the server, is to force a TimeoutError.
This leads to code that is hard to read and potentially unreliable if the solve is particularly fast.

For example, here is a code sample adapted from the documentation:
```
submission_id = None

while True:
    try:
        if not submission_id:
            wcs_header = ast.solve_from_image('/path/to/image.fit', solve_timeout=1)
        else:
            wcs_header = ast.monitor_submission(submission_id, solve_timeout=120)
    except TimeoutError as e:
        submission_id = e.args[1]
    else:
        break
```

I propose that the solvers directly return the jobid and let the user call the `monitor_submission` method themselves. Here is the same code with the proposed change:
```
submission_id = ast.solve_from_image('/path/to/image.fit')

while True:
    try:
        wcs_header = ast.monitor_submission(submission_id, solve_timeout=120)
    except TimeoutError as e:
        continue
    else:
        break
```
diff --git a/CHANGES.rst b/CHANGES.rst
index 86f2c2c7..1028199e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -59,7 +59,7 @@ alma
 - Fixed bug #2489 in which blank URLs were being sent to the downloader [#2490]
 - Removed deprecated broken functions from ``alma.utils``. [#2331]
 
-astrometry.net
+astrometry_net
 ^^^^^^^^^^^^^^
 
 - Added a ``verbose=`` keyword argument to ``AstrometryNet`` to control whether or not
@@ -69,12 +69,11 @@ astrometry.net
   solved by constructing a source list internally before sending data to
   astrometry.net. [#2484]
 
-astrometry.net
-^^^^^^^^^^^^^^
-
 - Avoid duplicated warnings about API key and raise an error only when API key is
   needed but not set. [#2483]
 
+- Added ``return_submission_id`` keyword argument to ``monitor_submission()``. [#2685]
+
 cadc
 ^^^^
 
@@ -142,7 +141,7 @@ mast
 - Expanding ``Cutouts`` functionality to support making Hubble Advanced Product (HAP)
   cutouts via HAPCut. [#2613]
 
-- Expanding ``Cutouts`` functionality to support TICA HLSPs now available through 
+- Expanding ``Cutouts`` functionality to support TICA HLSPs now available through
   ``TesscutClass``. [##2668]
 
 oac
diff --git a/astroquery/astrometry_net/core.py b/astroquery/astrometry_net/core.py
index 448c8c0c..94fada0c 100644
--- a/astroquery/astrometry_net/core.py
+++ b/astroquery/astrometry_net/core.py
@@ -199,7 +199,7 @@ class AstrometryNetClass(BaseQuery):
                                  'values for {}'.format(scale_type, required_keys))
 
     def monitor_submission(self, submission_id, *,
-                           solve_timeout=TIMEOUT, verbose=True):
+                           solve_timeout=TIMEOUT, verbose=True, return_submission_id=False):
         """
         Monitor the submission for completion.
 
@@ -212,15 +212,19 @@ class AstrometryNetClass(BaseQuery):
             Time, in seconds, to wait for the astrometry.net solver to find
             a solution.
         verbose : bool, optional
-            Whether to print out information about the solving
+            Whether to print out information about the solving.
+        return_submission_id : bool, optional
+            Whether to return the Submission ID number.
 
         Returns
         -------
 
-        None or `astropy.io.fits.Header`
+        None or `~astropy.io.fits.Header` or (`~astropy.io.fits.Header`, str)
             The contents of the returned object depend on whether the solve
             succeeds or fails. If the solve succeeds the header with
-            the WCS solution generated by astrometry.net is returned. If the solve
+            the WCS solution generated by astrometry.net is returned. A tuple
+            containing WCS solution and Submission ID is return if the
+            return_submission_id parameter is set True. If the solve
             fails then an empty dictionary is returned. See below for the outcome
             if the solve times out.
 
@@ -268,11 +272,15 @@ class AstrometryNetClass(BaseQuery):
         else:
             # Try to future-proof a little bit
             raise RuntimeError('Unrecognized status {}'.format(status))
-        return wcs
+        if return_submission_id is False:
+            return wcs
+        else:
+            return (wcs, submission_id)
 
     def solve_from_source_list(self, x, y, image_width, image_height, *,
                                solve_timeout=TIMEOUT,
                                verbose=True,
+                               return_submission_id=False,
                                **settings
                                ):
         """
@@ -293,7 +301,9 @@ class AstrometryNetClass(BaseQuery):
             Time, in seconds, to wait for the astrometry.net solver to find
             a solution.
         verbose : bool, optional
-            Whether to print out information about the solving
+            Whether to print out information about the solving.
+        return_submission_id : bool, optional
+            Whether to return the Submission ID number.
 
         For a list of the remaining settings, use the method
         `~AstrometryNetClass.show_allowed_settings`.
@@ -318,7 +328,8 @@ class AstrometryNetClass(BaseQuery):
         submission_id = response_d['subid']
         return self.monitor_submission(submission_id,
                                        solve_timeout=solve_timeout,
-                                       verbose=verbose)
+                                       verbose=verbose,
+                                       return_submission_id=return_submission_id)
 
     def solve_from_image(self, image_file_path, *, force_image_upload=False,
                          ra_key=None, dec_key=None,
@@ -326,6 +337,7 @@ class AstrometryNetClass(BaseQuery):
                          fwhm=3, detect_threshold=5,
                          solve_timeout=TIMEOUT,
                          verbose=True,
+                         return_submission_id=False,
                          **settings):
         """
         Plate solve from an image, either by uploading the image to
@@ -367,7 +379,10 @@ class AstrometryNetClass(BaseQuery):
             a solution.
 
         verbose : bool, optional
-            Whether to print out information about the solving
+            Whether to print out information about the solving.
+
+        return_submission_id : bool, optional
+            Whether to return the Submission ID number.
 
         For a list of the remaining settings, use the method
         `~AstrometryNetClass.show_allowed_settings`.
@@ -432,6 +447,7 @@ class AstrometryNetClass(BaseQuery):
                                                ccd.header['naxis2'],
                                                solve_timeout=solve_timeout,
                                                verbose=verbose,
+                                               return_submission_id=return_submission_id,
                                                **settings)
         if response.status_code != 200:
             raise RuntimeError('Post of job failed')
@@ -439,7 +455,8 @@ class AstrometryNetClass(BaseQuery):
         submission_id = response_d['subid']
         return self.monitor_submission(submission_id,
                                        solve_timeout=solve_timeout,
-                                       verbose=verbose)
+                                       verbose=verbose,
+                                       return_submission_id=return_submission_id)
 
 
 # the default tool for users to interact with is an instance of the Class
diff --git a/astroquery/astrometry_net/tests/test_astrometry_net_remote.py b/astroquery/astrometry_net/tests/test_astrometry_net_remote.py
index 55dd5d7e..68ab0fbe 100644
--- a/astroquery/astrometry_net/tests/test_astrometry_net_remote.py
+++ b/astroquery/astrometry_net/tests/test_astrometry_net_remote.py
@@ -152,3 +152,36 @@ def test_solve_timeout_behavior():
         except TypeError:
             pass
         assert difference == 0
+
+
+@pytest.mark.skipif(not api_key, reason='API key not set.')
+@pytest.mark.remote_data
+def test_solve_from_image_default_behaviour():
+    # Test that solving by uploading an image works
+    a = AstrometryNet()
+    a.api_key = api_key
+    sources = Table.read(os.path.join(DATA_DIR, 'test-source-list.fit'))
+    # The image_width, image_height and crpix_center below are set to match the
+    # original solve on astrometry.net.
+    result = a.solve_from_source_list(sources['X'], sources['Y'],
+                                      4109, 4096,
+                                      crpix_center=True)
+
+    assert isinstance(result, fits.Header)
+
+
+@pytest.mark.skipif(not api_key, reason='API key not set.')
+@pytest.mark.remote_data
+def test_solve_from_image_with_return_submission_id():
+    # Test that solving by uploading an image works
+    a = AstrometryNet()
+    a.api_key = api_key
+    sources = Table.read(os.path.join(DATA_DIR, 'test-source-list.fit'))
+    # The image_width, image_height and crpix_center below are set to match the
+    # original solve on astrometry.net.
+    result = a.solve_from_source_list(sources['X'], sources['Y'],
+                                      4109, 4096,
+                                      crpix_center=True,
+                                      return_submission_id=True)
+
+    assert isinstance(result, tuple)
['astroquery/astrometry_net/core.py', 'astroquery/astrometry_net/tests/test_astrometry_net_remote.py', 'CHANGES.rst']