diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json
--- a/cashtab/package-lock.json
+++ b/cashtab/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "cashtab",
-    "version": "3.10.9",
+    "version": "3.10.10",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "cashtab",
-            "version": "3.10.9",
+            "version": "3.10.10",
             "dependencies": {
                 "@bitgo/utxo-lib": "^11.0.0",
                 "@zxing/browser": "^0.1.4",
diff --git a/cashtab/package.json b/cashtab/package.json
--- a/cashtab/package.json
+++ b/cashtab/package.json
@@ -1,6 +1,6 @@
 {
     "name": "cashtab",
-    "version": "3.10.9",
+    "version": "3.10.10",
     "private": true,
     "scripts": {
         "start": "node scripts/start.js",
diff --git a/cashtab/src/components/Agora/__tests__/index.test.js b/cashtab/src/components/Agora/__tests__/index.test.js
--- a/cashtab/src/components/Agora/__tests__/index.test.js
+++ b/cashtab/src/components/Agora/__tests__/index.test.js
@@ -241,7 +241,9 @@
         expect(screen.getByText('Token Offers')).toBeInTheDocument();
 
         // We see the token name and ticker above its PartialOffer after OrderBooks load
-        expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument();
+        expect(
+            await screen.findByText('Cachet (CACHET)', {}, { timeout: 3000 }),
+        ).toBeInTheDocument();
 
         // Because this offer was created by this wallet, we have the option to cancel it
         expect(
@@ -771,7 +773,9 @@
         expect(screen.getByText('Token Offers')).toBeInTheDocument();
 
         // We see all token names and tickers above their PartialOffers
-        expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument();
+        expect(
+            await screen.findByText('Cachet (CACHET)', {}, { timeout: 3000 }),
+        ).toBeInTheDocument();
         expect(await screen.findByText('Bull (BULL)')).toBeInTheDocument();
 
         // If we select the offer created by the Beta wallet, we see a buy button
diff --git a/cashtab/src/components/Agora/index.tsx b/cashtab/src/components/Agora/index.tsx
--- a/cashtab/src/components/Agora/index.tsx
+++ b/cashtab/src/components/Agora/index.tsx
@@ -30,6 +30,37 @@
     tokenIds: string[];
 }
 
+const askPolitelyForTokenInfo = async (
+    promises: Promise<void>[],
+    requestLimit: number,
+    intervalMs: number,
+) => {
+    if (!Array.isArray(promises) || promises.length === 0) {
+        return;
+    }
+    const requests = promises.length;
+    const batchSize = Math.floor(requests / requestLimit);
+    const batchCount = Math.floor(requests / batchSize) + 1;
+    for (let i = 0; i < batchCount; i++) {
+        const batchStart = i * batchSize;
+        const thisBatch =
+            i === batchCount - 1
+                ? // The last batch is whatever is left in the array
+                  promises.slice(batchStart)
+                : // Other batches are batchsize entries starting from i
+                  promises.slice(batchStart, batchStart + batchSize);
+
+        await Promise.all(thisBatch);
+
+        // Wait intervalMs before asking again
+        await new Promise(resolve => setTimeout(resolve, intervalMs));
+    }
+};
+
+// Params for batching requests to chronik on the Agora screen
+const POLITE_REQUEST_LIMIT = 200;
+const POLITE_INTERVAL_MS = 2000;
+
 const Agora: React.FC = () => {
     const userLocale = getUserLocale(navigator);
     const ContextValue = useContext(WalletContext);
@@ -45,6 +76,9 @@
     const pk = (wallet.paths.get(appConfig.derivationPath) as CashtabPathInfo)
         .pk;
 
+    // Use a state param to keep track of how many orderbooks we load at once
+    const [loadedOrderBooksCount, setLoadedOrderBooksCount] = useState(0);
+
     // active agora partial offers organized for rendering this screen
     const [activeOffersCashtab, setActiveOffersCashtab] =
         useState<null | CashtabActiveOffers>(null);
@@ -131,6 +165,35 @@
         }
     }, [allOrderBooksLoaded, switches]);
 
+    useEffect(() => {
+        if (activeOffersCashtab === null || allOrderBooksLoaded) {
+            // Do nothing if we have no active offers or if everything is loaded
+            return;
+        }
+
+        const loadMoreOrderBooks = () => {
+            setLoadedOrderBooksCount(prevCount => {
+                const newCount = prevCount + POLITE_REQUEST_LIMIT;
+                // Only increase if there are more to load
+                if (
+                    newCount <=
+                    activeOffersCashtab.offeredFungibleTokenIds.length
+                ) {
+                    return newCount;
+                }
+                // Clear the interval when all are loaded
+                clearInterval(intervalId);
+                // Use the total when we get there
+                return activeOffersCashtab.offeredFungibleTokenIds.length;
+            });
+        };
+
+        const intervalId = setInterval(loadMoreOrderBooks, POLITE_INTERVAL_MS);
+
+        // Clean up the interval when component unmounts or when all order books are loaded
+        return () => clearInterval(intervalId);
+    }, [activeOffersCashtab, allOrderBooksLoaded]);
+
     /**
      * Specialized helper function to support use of Promise.all in adding new tokens to cache
      * While this functionality could be extended to other parts of Cashtab, for now it is
@@ -261,7 +324,11 @@
             );
         }
         try {
-            await Promise.all(tokenInfoPromises);
+            await askPolitelyForTokenInfo(
+                tokenInfoPromises,
+                POLITE_REQUEST_LIMIT,
+                POLITE_INTERVAL_MS,
+            );
         } catch (err) {
             console.error(`Error in Promise.all(tokenInfoPromises)`, err);
             // Cache will not be updated, token names and IDs will show spinners
@@ -439,8 +506,12 @@
                                             .offeredFungibleTokenIds.length >
                                         0 ? (
                                             <OfferTable>
-                                                {activeOffersCashtab.offeredFungibleTokenIds.map(
-                                                    offeredTokenId => {
+                                                {activeOffersCashtab.offeredFungibleTokenIds
+                                                    .slice(
+                                                        0,
+                                                        loadedOrderBooksCount,
+                                                    )
+                                                    .map(offeredTokenId => {
                                                         return (
                                                             <OrderBook
                                                                 key={
@@ -457,8 +528,7 @@
                                                                 }
                                                             />
                                                         );
-                                                    },
-                                                )}
+                                                    })}
                                             </OfferTable>
                                         ) : (
                                             <p>