diff --git a/instagram_utils/services.py b/instagram_utils/services.py
index 6c4349e4edbb5ec220458a7770ad7865e99702a4..077f25fa8863f0a59c065abf4686a6dacbac42b8 100644
--- a/instagram_utils/services.py
+++ b/instagram_utils/services.py
@@ -1,5 +1,5 @@
 import logging
-import requests
+import requests_cache
 
 from main.models import MainHomePage, MainPersonPage
 
@@ -15,36 +15,50 @@ class InstagramDownloadService:
     """
 
     def __init__(self, app_id: int, app_secret: str):
+        self.session = requests_cache.CachedSession("instagram_cache")
+        
         self.app_id = app_id
         self.app_secret = app_secret
 
-        # https://www.instagram.com/web/search/topsearch/?context=user&count=1&query=that_snowden
-        # Useful Instagram username search API
+    def get_user_info_list(self) -> list[str]:
+        access_block = MainHomePage.objects.first().instagram_access
 
-    def get_username_list(self) -> list[str]:
-        instagram_usernames_block = MainHomePage.objects.first().instagram_usernames
+        # TODO - Individual users' instagram info
 
-        person_username_list = (
-            MainPersonPage.objects.filter(instagram_username__isnull=False)
-            .values_list("instagram_username", flat=True)
-            .distinct()
-        )
-        homepage_username_list = [
-            username_data["value"] for username_data in instagram_usernames_block.raw_data
+        return [
+            (
+                block["value"]["name"],
+                block["value"]["user_id"],
+                block["value"]["access_token"]
+            )
+            for block in access_block.raw_data
         ]
 
-        # kvůli duplicitám udělám(e) list/set/list konverzi
-        return list({*person_username_list, *homepage_username_list})
+    def parse_media_for_user(self, name, user_id, access_token):
+        with self.session.cache_disabled():
+            recent_media = self.session.get(
+                f"https://graph.instagram.com/v16.0/{user_id}/media?access_token="
+                f"{access_token}&fields=caption,media_type,permalink,media_url,"
+                "thumbnail_url"
+            )
 
-    def convert_usernames_to_ids(self, username_list) -> list[str]:
-        user_ids = []
+        if not recent_media.ok:
+            logger.warning(
+                "Error getting media for user %s: %s",
+                user_id,
+                recent_media.status_code
+            )
 
-        for username in username_list:
-            print(username)
+            return
 
-        return user_ids
+        recent_media = recent_media.json()
+
+        print(recent_media)
 
     def perform_update(self) -> None:
-        username_list = self.get_username_list()
+        user_info_list = self.get_user_info_list()
+
+        media = []
 
-        user_ids = self.convert_usernames_to_ids(username_list)
+        for user_info in user_info_list:
+            media.append(self.parse_media_for_user(*user_info))
diff --git a/main/blocks.py b/main/blocks.py
index f0ffe7fe26f04414518c8366f561f88e9be3c702..2e039e8ccd2903a4e758581f06f4c8288faa207a 100644
--- a/main/blocks.py
+++ b/main/blocks.py
@@ -372,4 +372,18 @@ class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin):
         label = "Karty odkazĹŻ s nadpisem"
 
 
+class InstagramAccessBlock(StructBlock):
+    name = CharBlock(label="Zobrazované jméno")
+    user_id = CharBlock(label="Uživatelské ID")
+    access_token = CharBlock(label="Přístupový token")
+
+    class Meta:
+        label = "Synchronizace s Instagramem"
+        help_text = (
+            "Informace lze získat přihlášením požadovaným Instagramovým "
+            "účtem na tools.pirati.cz/instagram . Token je třeba kvůli "
+            "podmínkám Instagramu každých 60 dní obnovit."
+        )
+
+
 # TwitterCarouselBlock
diff --git a/main/migrations/0047_remove_mainhomepage_instagram_usernames_and_more.py b/main/migrations/0047_remove_mainhomepage_instagram_usernames_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ffb82cca00a53943819f00197da88817b34a591
--- /dev/null
+++ b/main/migrations/0047_remove_mainhomepage_instagram_usernames_and_more.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.1.6 on 2023-04-05 11:17
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0046_mainhomepage_instagram_usernames_and_more'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='mainhomepage',
+            name='instagram_usernames',
+        ),
+        migrations.AddField(
+            model_name='mainhomepage',
+            name='instagram_access',
+            field=wagtail.fields.StreamField([('instagram_access', wagtail.blocks.StructBlock([('username', wagtail.blocks.CharBlock(label='Uživatelské jméno')), ('access_token', wagtail.blocks.CharBlock(help_text='TODO', label='Přístupový token'))]))], blank=True, use_json_field=True, verbose_name='Uživatelská jména a přístupové tokeny pro synchronizované Instagram účty'),
+        ),
+    ]
diff --git a/main/migrations/0048_alter_mainhomepage_instagram_access.py b/main/migrations/0048_alter_mainhomepage_instagram_access.py
new file mode 100644
index 0000000000000000000000000000000000000000..808610fa2221c5d48fd9ecfa370fe83ff4cc95ee
--- /dev/null
+++ b/main/migrations/0048_alter_mainhomepage_instagram_access.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.6 on 2023-04-05 11:34
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0047_remove_mainhomepage_instagram_usernames_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mainhomepage',
+            name='instagram_access',
+            field=wagtail.fields.StreamField([('instagram_access', wagtail.blocks.StructBlock([('name', wagtail.blocks.CharBlock(label='Zobrazované jméno')), ('user_id', wagtail.blocks.CharBlock(help_text='TODO', label='Uživatelské ID')), ('access_token', wagtail.blocks.CharBlock(help_text='TODO', label='Přístupový token'))]))], blank=True, use_json_field=True, verbose_name='Uživatelská jména a přístupové tokeny pro synchronizované Instagram účty'),
+        ),
+    ]
diff --git a/main/migrations/0049_alter_mainhomepage_instagram_access.py b/main/migrations/0049_alter_mainhomepage_instagram_access.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ffa876b5842cbd976731c072c0b5b62cf7c2527
--- /dev/null
+++ b/main/migrations/0049_alter_mainhomepage_instagram_access.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.6 on 2023-04-05 11:56
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0048_alter_mainhomepage_instagram_access'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mainhomepage',
+            name='instagram_access',
+            field=wagtail.fields.StreamField([('instagram_access', wagtail.blocks.StructBlock([('name', wagtail.blocks.CharBlock(label='Zobrazované jméno')), ('user_id', wagtail.blocks.CharBlock(label='Uživatelské ID')), ('access_token', wagtail.blocks.CharBlock(label='Přístupový token'))]))], blank=True, use_json_field=True, verbose_name='Uživatelská jména a přístupové tokeny pro synchronizované Instagram účty'),
+        ),
+    ]
diff --git a/main/models.py b/main/models.py
index 7a330821f03d6b8d0df06f4269d05a7f64a02979..2f2fba721676a408c8741f6ea4ab5bcf47a01037 100644
--- a/main/models.py
+++ b/main/models.py
@@ -142,9 +142,9 @@ class MainHomePage(
         use_json_field=True,
     )
 
-    instagram_usernames = StreamField(
-        [("username", CharBlock(label="Instagram uživatelské jméno"))],
-        verbose_name="Uživatelská jména pro synchronizované Instagram účty",
+    instagram_access = StreamField(
+        [("instagram_access", blocks.InstagramAccessBlock())],
+        verbose_name="Uživatelská jména a přístupové tokeny pro synchronizované Instagram účty",
         blank=True,
         max_num=64,
         use_json_field=True,
@@ -167,7 +167,7 @@ class MainHomePage(
         FieldPanel("social_links"),
         FieldPanel("matomo_id"),
         FieldPanel("twitter_usernames"),
-        FieldPanel("instagram_usernames"),
+        FieldPanel("instagram_access"),
     ]
 
     ### EDIT HANDLERS
diff --git a/redmine_cache.sqlite b/redmine_cache.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..3b5e89721f01b173248305cbb9b09457108c9a84
Binary files /dev/null and b/redmine_cache.sqlite differ
diff --git a/requirements/base.in b/requirements/base.in
index 0cd088a7fe2c0e5a0ed83397828e20fac0a2532f..65386446adf23c7101e65712581b4d13d17bbcf0 100644
--- a/requirements/base.in
+++ b/requirements/base.in
@@ -26,3 +26,4 @@ pypdf2
 pyyaml
 fastjsonschema
 tweepy
+requests-cache
diff --git a/requirements/base.txt b/requirements/base.txt
index 5541583025f8eea6586ffb210fcf2056f8beb4dd..9544a1936c1b0d6b23f7f9ca4f9e2fb2a0673e22 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile base.in
@@ -19,7 +19,10 @@ asttokens==2.2.1
 async-timeout==4.0.2
     # via redis
 attrs==22.2.0
-    # via ics
+    # via
+    #   cattrs
+    #   ics
+    #   requests-cache
 backcall==0.2.0
     # via ipython
 beautifulsoup4==4.11.2
@@ -32,6 +35,8 @@ bleach==6.0.0
     # via -r base.in
 brotli==1.0.9
     # via fonttools
+cattrs==22.2.0
+    # via requests-cache
 celery==5.2.7
     # via -r base.in
 certifi==2022.12.7
@@ -171,6 +176,8 @@ pillow==9.4.0
     #   weasyprint
 pirates==0.6.0
     # via -r base.in
+platformdirs==3.2.0
+    # via requests-cache
 prompt-toolkit==3.0.36
     # via
     #   click-repl
@@ -218,9 +225,12 @@ requests==2.28.2
     # via
     #   -r base.in
     #   mozilla-django-oidc
+    #   requests-cache
     #   requests-oauthlib
     #   tweepy
     #   wagtail
+requests-cache==1.0.1
+    # via -r base.in
 requests-oauthlib==1.3.1
     # via tweepy
 sentry-sdk==1.15.0
@@ -234,6 +244,7 @@ six==1.16.0
     #   ics
     #   l18n
     #   python-dateutil
+    #   url-normalize
 soupsieve==2.3.2.post1
     # via beautifulsoup4
 sqlparse==0.4.3
@@ -254,9 +265,12 @@ traitlets==5.9.0
     #   matplotlib-inline
 tweepy==4.12.1
     # via -r base.in
+url-normalize==1.4.3
+    # via requests-cache
 urllib3==1.26.14
     # via
     #   requests
+    #   requests-cache
     #   sentry-sdk
 vine==5.0.0
     # via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 16d15b0fe79ceff9587f5ac290b7637ddff5f472..d37f0c97294d7a7aac1553fdea6e8454da315a75 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,6 +1,6 @@
 #
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.11
+# by the following command:
 #
 #    pip-compile dev.in
 #
@@ -16,8 +16,6 @@ django==4.1.6
     #   django-debug-toolbar
 django-debug-toolbar==3.8.1
     # via -r dev.in
-exceptiongroup==1.1.0
-    # via pytest
 factory-boy==3.2.1
     # via pytest-factoryboy
 faker==16.7.0
@@ -75,10 +73,6 @@ termcolor==2.2.0
     # via
     #   pytest-sugar
     #   snapshottest
-tomli==2.0.1
-    # via
-    #   coverage
-    #   pytest
 typing-extensions==4.4.0
     # via pytest-factoryboy
 wasmer==1.1.0
diff --git a/requirements/production.txt b/requirements/production.txt
index 3333386f4e89a4345670321d51b646a4098aa598..992947ab06bdebf5c6edc9fe1cbc51c9b1b34600 100644
--- a/requirements/production.txt
+++ b/requirements/production.txt
@@ -1,6 +1,6 @@
 #
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.11
+# by the following command:
 #
 #    pip-compile production.in
 #