C# — Как сделать выборку из текста?
https://www.newtonsoft.com/json
если вам предложат регулярки, стоит сравнивать производительность (регулярки не блещут скоростью upd все верно, простотой тоже, тривиальные случаи не считаем )))
сам бы скорее всего сделал в рукопашку, при условии что другие данные точно не нужны. при чем суть вы уже описали ;))) и по производительности, это наверняка самый быстрый вариант (upd )по производительности, а вот по реализации - зависит от навыков, кому то может быть проще newtonsoft (ну и см ленивый вариант, там тоже не производительность (но думаю и далеко не самый тормозной), но легко быстро набросать логику и легко ее оттестировать)
ps можно еще ленивый вариант - Split по {"userId":" тогда первый элемент отбрасываем, все остальные начинаются с искомой величины, надо только хвост "} bla-bla-bla отрубить
ps держите прототип, содержит все 3 упомянутые подхода, но перфоманс можно сравнить только на реально больших данных )).. (но на больших данных могут и баги полезть ;)spoiler
вопрос понравился, и выдалось время.. хотя и не за 15 минут, врать не буду, часа полтора порядка 45 минут ушло на заковырки )) в основном с json (мало опыта)upd и уже исправлено - была заготовка класса для частичной json выборки, но она не нужнаusing Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace parsejson111
{
    class Program
    {
        const string data = "{\"anchor\":\"aaabbbccc==\",\"members\":[{\"userId\":\"111\"},{\"userId\":\"2222\"},{\"userId\":\"333\"},{\"userId\":\"444\"}],\"has_more\":true}";
        // фишки замера заложены, но разница может проявиться только на больших объемах ))

        static void Main(string[] args)
        {
            Console.WriteLine("data:");
            Console.WriteLine(data);

            var sw = new Stopwatch();
            sw.Start();
            var ts = sw.Elapsed; // лень объявлять тип

            json();
            hand();
            lazy();

            sw.Stop();
            Console.WriteLine("press any key to continue...");
            Console.ReadKey();

            void json() // возможно существует более элегантный вариант. тут у меня опыта мало ))
            {
                sw.Reset();
                Console.WriteLine("... json");
                var j = JObject.Parse(data);
                var r = j["members"].Children().ToList();
                foreach (var u in r)
                {
                    var uid = JObject.Parse(u.ToString());
                    var id = uid["userId"].Value<string>();
                    Console.WriteLine(id);
                }
                ts = sw.Elapsed;
                Console.WriteLine($"\t{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds:000}");
            }

            void hand() // зато тут гарантирую - оптимизировать лучше не реально, и по перфомансу вряд ли можно лучше ))
            {
                sw.Reset();
                Console.WriteLine("... hand");
                var p = 0;
                while ((p = data.IndexOf("{\"userId\":\"", p)) > 0)
                {
                    p += 11; //длина шаблона. заодно избегаем риска зацикливания
                    var f = data.IndexOf("\"}", p);
                    Console.WriteLine(data.Substring(p, f - p));
                }
                ts = sw.Elapsed;
                Console.WriteLine($"\t{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds:000}");
            }

            void lazy()
            {
                sw.Reset();
                Console.WriteLine("... lazy");
                var t = data.Split(new string[] { "{\"userId\":\"" }, StringSplitOptions.RemoveEmptyEntries);
                for (var i = 1; i < t.Length; i++) // здесь и везде - обработка пустого множества на вашей совести
                {
                    var id = t[i].Split(new string[] { "\"}" }, StringSplitOptions.RemoveEmptyEntries);
                    Console.WriteLine(id[0]);
                }
                ts = sw.Elapsed;
                Console.WriteLine($"\t{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds:000}");
            }
        }
    }
}
Регулярками разбирать не стоит, можно наступить на грабли.
Лучше разбирать при помощи System.Json или Json.NET (Newtonsoft).