diff --git a/server.js b/server.js
index 40e0741..14fee11 100644
--- a/server.js
+++ b/server.js
@@ -53,6 +53,23 @@ const s3Client = new S3Client({
 const uploadDir = 'uploads';
 const uploadMulter = multer({ dest: uploadDir + '/' });
 
+app.get('/ping', async (req, res) => {
+    res.send('pong');
+});
+
+app.get('/image-limits', async (req, res) => {
+  limitsResult = await retrievelimits(req, res);
+  if (!limitsResult.success) {
+    return limitsResult.result;
+  }
+  return res.status(200).send(JSON.stringify({
+    success: true,
+    doneImagesThisWeek: limitsResult.doneImagesThisWeek,
+    maxImagesPerWeek: limitsResult.maxImagesPerWeek,
+    nextWeekBeginDateTime: limitsResult.nextWeekBeginDateTime
+  }));
+});
+
 // POST endpoint to upload an image
 app.post('/image', uploadMulter.single('image'), async (req, res) => {
   const reqFile = req.file;
@@ -69,78 +86,16 @@ app.post('/image', uploadMulter.single('image'), async (req, res) => {
   }
 
   try {
-    const decoded = await decodeJwt(req, res)
-    if (!decoded.success) {
-      return decoded.result;
-    }
-    const issuerDid = decoded.issuerDid;
-    const jwt = decoded.jwt;
-
-    // Check the user's limits, first from the DB and then from the server
-    let limitPerWeek = await new Promise((resolve, reject) => {
-      db.get(
-        'SELECT per_week FROM user WHERE did = ?',
-        [ issuerDid ],
-        (dbErr, row) => {
-          if (dbErr) {
-            console.error('Error getting user record from database (but continuing):', dbErr)
-            // may not matter, so continue
-          }
-          resolve(row?.per_week);
-        }
-      );
-    });
-    if (limitPerWeek == null) {
-      const headers = {
-        'Authorization': `Bearer ${jwt}`,
-        'Content-Type': 'application/json'
-      }
-      const response = await fetch(endorserApiUrl + '/api/report/rateLimits', { headers });
-      if (response.status !== 200) {
-        console.error("Got bad response of", response.status, "when checking rate limits for", issuerDid);
-        return res.status(400).send(JSON.stringify({ success: false, message: 'Got bad status of ' + response.status + ' when checking limits with endorser server. Verify that the account exists and that the JWT works for that server.'}));
-      } else {
-        const body = await response.json();
-        limitPerWeek = body.maxClaimsPerWeek / 4; // allowing fewer images than claims
-
-        await new Promise((resolve, reject) => {
-          db.run(
-            'INSERT INTO user (did, per_week) VALUES (?, ?)',
-            [issuerDid, limitPerWeek],
-            (dbErr) => {
-              if (dbErr) {
-                console.error("Error inserting user record for", issuerDid, "into database (but continuing):", dbErr);
-                // we can continue... it just means we'll check the endorser server again next time
-              }
-              resolve();
-            }
-          );
-        });
-      }
-    }
-
-    if (limitPerWeek == null) {
-      return res.status(400).send(JSON.stringify({ success: false, message: 'Unable to determine rate limits for this user. Verify that the account exists and that the JWT works for that server.' }));
+    limitsResult = await retrievelimits(req, res);
+    if (!limitsResult.success) {
+      return limitsResult.result;
     }
+    const doneImagesThisWeek = limitsResult.doneImagesThisWeek;
+    const maxImagesPerWeek = limitsResult.maxImagesPerWeek;
+    const issuerDid = limitsResult.issuerDid;
 
-    // check the user's claims so far this week
-    const startOfWeekDate = DateTime.utc().startOf('week') // luxon weeks start on Mondays
-    const startOfWeekString = startOfWeekDate.toISO()
-    const imagesCount = await new Promise((resolve, reject) => {
-      db.get(
-        'SELECT COUNT(*) AS week_count FROM image WHERE did = ? AND time >= ?',
-        [issuerDid, startOfWeekString],
-        (dbErr, row) => {
-          if (dbErr) {
-            console.error(currentDate, "Error counting records for", issuerDid, "into database (but continuing):", dbErr);
-            // we can continue... it just means we'll check the endorser server again next time
-          }
-          resolve(row?.week_count);
-        }
-      );
-    });
-    if (imagesCount >= limitPerWeek) {
-      return res.status(400).send(JSON.stringify({ success: false, message: 'You have reached your weekly limit of ' + limitPerWeek + ' images.' }));
+    if (doneImagesThisWeek >= maxImagesPerWeek) {
+      return res.status(400).send(JSON.stringify({ success: false, message: 'You have reached your weekly limit of ' + maxImagesPerWeek + ' images.' }));
     }
 
     // Read the file from the temporary location
@@ -350,6 +305,96 @@ app.delete('/image/:url', async (req, res) => {
   }
 });
 
+async function retrievelimits(req, res) {
+  const decoded = await decodeJwt(req, res)
+  if (!decoded.success) {
+    return decoded.result;
+  }
+  const issuerDid = decoded.issuerDid;
+  const jwt = decoded.jwt;
+
+  // Check the user's limits, first from the DB and then from the server
+  let maxImagesPerWeek = await new Promise((resolve, reject) => {
+    db.get(
+      'SELECT per_week FROM user WHERE did = ?',
+      [ issuerDid ],
+      (dbErr, row) => {
+        if (dbErr) {
+          console.error('Error getting user record from database (but continuing):', dbErr)
+          // may not matter, so continue
+        }
+        resolve(row?.per_week);
+      }
+    );
+  });
+  if (maxImagesPerWeek == null) {
+    const headers = {
+      'Authorization': `Bearer ${jwt}`,
+      'Content-Type': 'application/json'
+    }
+    const response = await fetch(endorserApiUrl + '/api/report/rateLimits', { headers });
+    if (response.status !== 200) {
+      console.error("Got bad response of", response.status, "when checking rate limits for", issuerDid);
+      return {
+        success: false,
+        result: res.status(400).send(JSON.stringify({ success: false, message: 'Got bad status of ' + response.status + ' when checking limits with endorser server. Verify that the account exists and that the JWT works for that server.'}))
+      };
+    } else {
+      const body = await response.json();
+      maxImagesPerWeek = body.maxClaimsPerWeek / 4; // allowing fewer images than claims
+
+      await new Promise((resolve, reject) => {
+        db.run(
+          'INSERT INTO user (did, per_week) VALUES (?, ?)',
+          [issuerDid, maxImagesPerWeek],
+          (dbErr) => {
+            if (dbErr) {
+              console.error("Error inserting user record for", issuerDid, "into database (but continuing):", dbErr);
+              // we can continue... it just means we'll check the endorser server again next time
+            }
+            resolve();
+          }
+        );
+      });
+    }
+  }
+
+  if (maxImagesPerWeek == null) {
+    return {
+      success: false,
+      result: res.status(400).send(JSON.stringify({ success: false, message: 'Unable to determine rate limits for this user. Verify that the account exists and that the JWT works for that server.' }))
+    };
+  }
+
+  // check the user's claims so far this week
+  const startOfWeekDate = DateTime.utc().startOf('week') // luxon weeks start on Mondays
+  const startOfWeekString = startOfWeekDate.toISO()
+  const imagesCount = await new Promise((resolve, reject) => {
+    db.get(
+      'SELECT COUNT(*) AS week_count FROM image WHERE did = ? AND time >= ?',
+      [issuerDid, startOfWeekString],
+      (dbErr, row) => {
+        if (dbErr) {
+          console.error(currentDate, "Error counting records for", issuerDid, "into database (but continuing):", dbErr);
+          // we can continue... it just means we'll check the endorser server again next time
+        }
+        resolve(row?.week_count);
+      }
+    );
+  });
+
+  const nextWeekBeginDateTime = startOfWeekDate.plus({ week: 1 }); // luxon weeks start on Mondays
+  const nextWeekBeginDateTimeString = nextWeekBeginDateTime.toISO()
+
+  return {
+    success: true,
+    doneImagesThisWeek: imagesCount,
+    maxImagesPerWeek: maxImagesPerWeek,
+    nextWeekBeginDateTime: nextWeekBeginDateTimeString,
+    issuerDid: issuerDid,
+  };
+}
+
 /**
  * retrieve Bearer JWT from Authorization header and return either:
  * { success: true, issuerDid: string, jwt: string }
@@ -371,10 +416,7 @@ async function decodeJwt(req, res) {
     console.error(errorTime, 'Got invalid JWT in Authorization header:', verified);
     return {
       success: false,
-      result: res.status(401).send(JSON.stringify({
-        success: false,
-        message: 'Got invalid JWT in Authorization header. See server logs at ' + errorTime
-      }))
+      result: res.status(401).send(JSON.stringify({ success: false, message: 'Got invalid JWT in Authorization header. See server logs at ' + errorTime }))
     };
   }
   return { success: true, issuerDid: verified.issuer, jwt: jwt };