diff --git a/instagram_utils/migrations/0001_initial.py b/instagram_utils/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe92a5f0cceab4237757af27655e75ed54fd1932
--- /dev/null
+++ b/instagram_utils/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# Generated by Django 4.1.6 on 2023-04-05 17:16
+
+from django.db import migrations, models
+import instagram_utils.models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='InstagramPost',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('author_name', models.CharField(max_length=64, verbose_name='Jméno autora')),
+                ('author_username', models.CharField(max_length=64, verbose_name='Username autora')),
+                ('timestamp', models.DateTimeField(default=instagram_utils.models.get_current_datetime, verbose_name='Datum a čas vytvoření')),
+                ('caption', models.TextField(blank=True, null=True, verbose_name='Popis')),
+                ('image', models.ImageField(upload_to='instagram', verbose_name='Obrázek')),
+                ('url', models.URLField(blank=True, null=True, verbose_name='Odkaz')),
+            ],
+            options={
+                'ordering': ('timestamp',),
+            },
+        ),
+    ]
diff --git a/instagram_utils/migrations/0002_instagrampost_remote_id.py b/instagram_utils/migrations/0002_instagrampost_remote_id.py
new file mode 100644
index 0000000000000000000000000000000000000000..61fa72432662fda4fb794ceac0dbc9c4e79d4b5a
--- /dev/null
+++ b/instagram_utils/migrations/0002_instagrampost_remote_id.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.1.6 on 2023-04-05 17:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('instagram_utils', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='instagrampost',
+            name='remote_id',
+            field=models.CharField(default='', max_length=64, verbose_name='ID Postu'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/instagram_utils/migrations/0003_alter_instagrampost_remote_id.py b/instagram_utils/migrations/0003_alter_instagrampost_remote_id.py
new file mode 100644
index 0000000000000000000000000000000000000000..8053396e28fbb1655ca1baf9275272949fe823b6
--- /dev/null
+++ b/instagram_utils/migrations/0003_alter_instagrampost_remote_id.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.1.6 on 2023-04-05 17:19
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('instagram_utils', '0002_instagrampost_remote_id'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='instagrampost',
+            name='remote_id',
+            field=models.CharField(max_length=64, unique=True, verbose_name='ID Postu'),
+        ),
+    ]
diff --git a/instagram_utils/migrations/__init__.py b/instagram_utils/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/instagram_utils/models.py b/instagram_utils/models.py
index 47c408ae0ec14ec869968176eb851da5d884d69f..eb72e8bd2a165c2511e89c5f8ae292322015567c 100644
--- a/instagram_utils/models.py
+++ b/instagram_utils/models.py
@@ -1,9 +1,54 @@
+import datetime
+
 from django.db import models
 
 
+def get_current_datetime() -> datetime.datetime:
+    return datetime.datetime.now(tz=datetime.timezone.utc)
+
+
 class InstagramPost(models.Model):
     """
     Model representing an Instgram post obtained from its API through the
     update_instagram management command.
     """
 
+    remote_id = models.CharField(
+        verbose_name="ID Postu",
+        max_length=64,
+        unique=True,
+    )
+    timestamp = models.DateTimeField(
+        verbose_name="Datum a čas vytvoření",
+        default=get_current_datetime
+    )
+
+    author_name = models.CharField(
+        verbose_name="Jméno autora",
+        max_length=64,
+    )
+    author_username = models.CharField(
+        verbose_name="Username autora",
+        max_length=64,
+    )
+
+    caption = models.TextField(
+        verbose_name="Popis",
+        blank=True,
+        null=True,
+    )
+    image = models.ImageField(
+        verbose_name="Obrázek",
+        upload_to="instagram",
+    )
+    url = models.URLField(
+        verbose_name="Odkaz",
+        blank=True,
+        null=True,
+    )
+
+    def __str__(self) -> str:
+        return f"@{self.author_username} - {self.caption}"
+
+    class Meta:
+        ordering = ("timestamp",)
diff --git a/instagram_utils/services.py b/instagram_utils/services.py
index 077f25fa8863f0a59c065abf4686a6dacbac42b8..f7246c9a6392421b109df2de70e55acf8052c57f 100644
--- a/instagram_utils/services.py
+++ b/instagram_utils/services.py
@@ -1,6 +1,10 @@
+import datetime
 import logging
+import io
+import os
 import requests_cache
 
+from django.core.files import File
 from main.models import MainHomePage, MainPersonPage
 
 from .models import InstagramPost
@@ -28,37 +32,101 @@ class InstagramDownloadService:
         return [
             (
                 block["value"]["name"],
-                block["value"]["user_id"],
                 block["value"]["access_token"]
             )
             for block in access_block.raw_data
         ]
 
-    def parse_media_for_user(self, name, user_id, access_token):
+    def download_remote_image(self, image_url) -> (str, File):
+        try:
+            response = self.session.get(image_url)
+            response.raise_for_status()
+        except Exception as exc:
+            logger.warning(
+                "Error getting Instagram image at %s: %s",
+                image_url, exc
+            )
+            return "", None
+
+        return os.path.basename(image_url), File(io.BytesIO(response.content))
+
+    def get_user_data(self, access_token: str) -> dict:
+        user_data = self.session.get(
+            f"https://graph.instagram.com/v16.0/me?access_token={access_token}"
+            "&fields=id,username"
+        )
+        user_data.raise_for_status()
+
+        return user_data.json()
+
+    def get_recent_media(self, user_data: dict, access_token: str) -> list[dict]:
         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"
+                f"https://graph.instagram.com/v16.0/{user_data['id']}/media?access_token="
+                f"{access_token}&fields=id,timestamp,caption,media_type,permalink,"
+                "media_url,thumbnail_url"
             )
 
         if not recent_media.ok:
             logger.warning(
                 "Error getting media for user %s: %s",
-                user_id,
+                user_data["id"],
                 recent_media.status_code
             )
 
+            return []
+
+        logger.debug("Parsing Instagram feed: %s", recent_media)
+
+        return recent_media.json()["data"]
+
+    def parse_media_for_user(self, name: str, access_token: str) -> None:
+        user_data = self.get_user_data(access_token)
+        recent_media_json = self.get_recent_media(user_data, access_token)
+
+        if len(recent_media_json) == 0:
             return
 
-        recent_media = recent_media.json()
+        posts = []
+
+        for media_data in recent_media_json:
+            # Don't recreate existing posts'
+            if InstagramPost.objects.filter(remote_id=media_data["id"]).exists():
+                logging.info(
+                    "Skipping Instagram post ID %s, already exists",
+                    media_data["id"]
+                )
+
+                continue
+
+            post = InstagramPost(
+                remote_id=media_data["id"],
+                author_name=name,
+                author_username=user_data["username"],
+                timestamp=datetime.datetime.strptime(
+                    media_data["timestamp"],
+                    "%Y-%m-%dT%H:%M:%S%z",
+                ),
+                caption=media_data["caption"],
+                url=media_data["permalink"],
+            )
 
-        print(recent_media)
+            post.image.save(
+                *self.download_remote_image(media_data["media_url"]),
+                False,  # Don't save yet
+            )
+
+            post.save()
+
+            logger.info(
+                "Saved Instagram post ID %s",
+                post.remote_id,
+            )
 
     def perform_update(self) -> None:
         user_info_list = self.get_user_info_list()
 
-        media = []
+        media_list = []
 
         for user_info in user_info_list:
-            media.append(self.parse_media_for_user(*user_info))
+            self.parse_media_for_user(*user_info)
diff --git a/main/blocks.py b/main/blocks.py
index 2e039e8ccd2903a4e758581f06f4c8288faa207a..f9266c4b06cd0f067614b6c5f8c825fddeaa6414 100644
--- a/main/blocks.py
+++ b/main/blocks.py
@@ -374,7 +374,6 @@ class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin):
 
 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:
diff --git a/main/migrations/0050_alter_mainhomepage_instagram_access.py b/main/migrations/0050_alter_mainhomepage_instagram_access.py
new file mode 100644
index 0000000000000000000000000000000000000000..decd471b58ada9c413d454d11ca652a70cb7dcfe
--- /dev/null
+++ b/main/migrations/0050_alter_mainhomepage_instagram_access.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.6 on 2023-04-05 16:45
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0049_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')), ('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/redmine_cache.sqlite b/redmine_cache.sqlite
deleted file mode 100644
index 3b5e89721f01b173248305cbb9b09457108c9a84..0000000000000000000000000000000000000000
Binary files a/redmine_cache.sqlite and /dev/null differ