diff --git a/Makefile.am b/Makefile.am
index 3113453..48a1527 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,6 +58,7 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-apple-trailers \
                             --enable-tmdb \
                             --enable-tracker \
                             --enable-vimeo \
+                            --enable-vkontakte \
                             --enable-youtube
 
 distclean-local:
diff --git a/configure.ac b/configure.ac
index 703e4c8..3ee4dde 100644
--- a/configure.ac
+++ b/configure.ac
@@ -915,6 +915,57 @@ DEPS_VIMEO_LIBS="$DEPS_LIBS $GRLNET_LIBS $XML_LIBS $TOTEM_PL_PARSER_LIBS"
 AC_SUBST(DEPS_VIMEO_LIBS)
 
 # ----------------------------------------------------------
+# BUILD VKONTAKTE PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(vkontakte,
+        AC_HELP_STRING([--enable-vkontakte],
+                [enable VKontakte plugin (default: auto)]),
+        [
+                case "$enableval" in
+                     yes)
+                        if test "x$HAVE_GRLNET" = "xno"; then
+                           AC_MSG_ERROR([grilo-net-0.2 >= 0.2.2 not found, install it or use --disable-vkontakte])
+                        fi
+                        if test "x$HAVE_JSON_GLIB" = "xno"; then
+                           AC_MSG_ERROR([json-glib-1.0 not found, install it or use --disable-vkontakte])
+                        fi
+                        ;;
+                esac
+        ],
+        [
+                if test "x$HAVE_GRLNET" = "xyes" -a "x$HAVE_JSON_GLIB" = "xyes"; then
+                   enable_vkontakte=yes
+                else
+                   enable_vkontakte=no
+                fi
+        ])
+
+AM_CONDITIONAL([VKONTAKTE_PLUGIN], [test "x$enable_vkontakte" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL vkontakte"
+if test "x$enable_vkontakte" = "xyes"
+then
+	GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED vkontakte"
+fi
+
+VKONTAKTE_PLUGIN_ID="grl-vkontakte"
+AC_SUBST(VKONTAKTE_PLUGIN_ID)
+AC_DEFINE_UNQUOTED([VKONTAKTE_PLUGIN_ID], ["$VKONTAKTE_PLUGIN_ID"], [VKontakte plugin ID])
+
+DEPS_VKONTAKTE_CFLAGS="$DEPS_CFLAGS $JSON_CFLAGS $GRLNET_CFLAGS"
+if test "x$enable_goa" = "xyes"; then
+	DEPS_VKONTAKTE_CFLAGS="$DEPS_VKONTAKTE_CFLAGS $GOA_CFLAGS"
+fi
+AC_SUBST(DEPS_VKONTAKTE_CFLAGS)
+
+
+DEPS_VKONTAKTE_LIBS="$DEPS_LIBS $JSON_LIBS $GRLNET_LIBS"
+if test "x$enable_goa" = "xyes"; then
+	DEPS_VKONTAKTE_LIBS="$DEPS_VKONTAKTE_LIBS $GOA_LIBS"
+fi
+AC_SUBST(DEPS_VKONTAKTE_LIBS)
+
+# ----------------------------------------------------------
 # BUILD GRAVATAR PLUGIN
 # ----------------------------------------------------------
 
@@ -1442,6 +1493,7 @@ AC_CONFIG_FILES([
   src/tmdb/Makefile
   src/tracker/Makefile
   src/vimeo/Makefile
+  src/vkontakte/Makefile
   src/youtube/Makefile
   tests/Makefile
   tests/apple-trailers/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c373a8e..c7c4774 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -26,4 +26,5 @@ src/tracker/grl-tracker-source-api.c
 src/tracker/grl-tracker-source-priv.h
 src/tracker/grl-tracker-utils.c
 src/vimeo/grl-vimeo.c
+src/vkontakte/grl-vkontakte.c
 src/youtube/grl-youtube.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 67be649..e9ab701 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,7 +59,7 @@ SUBDIRS += lua-factory
 endif
 
 if MAGNATUNE_PLUGIN
-SUBDIRS += magnatune 
+SUBDIRS += magnatune
 endif
 
 if METADATA_STORE_PLUGIN
@@ -102,6 +102,10 @@ if VIMEO_PLUGIN
 SUBDIRS += vimeo
 endif
 
+if VKONTAKTE_PLUGIN
+SUBDIRS += vkontakte
+endif
+
 if YOUTUBE_PLUGIN
 SUBDIRS += youtube
 endif
@@ -117,7 +121,7 @@ endif
 DIST_SUBDIRS = \
    apple-trailers bliptv bookmarks dleyna dmap filesystem flickr freebox gravatar jamendo \
    lastfm-albumart local-metadata lua-factory magnatune metadata-store opensubtitles \
-   optical-media pocket podcasts raitv shoutcast thetvdb tmdb tracker vimeo youtube
+   optical-media pocket podcasts raitv shoutcast thetvdb tmdb tracker vimeo vkontakte youtube
 
 MAINTAINERCLEANFILES =	\
         *.in		\
diff --git a/src/vkontakte/Makefile.am b/src/vkontakte/Makefile.am
new file mode 100644
index 0000000..fec4518
--- /dev/null
+++ b/src/vkontakte/Makefile.am
@@ -0,0 +1,37 @@
+#
+# Makefile.am
+#
+# Author: George Sedov <radist.morse@gmail.com>
+#
+# Copyright (C) 2015 George Sedov. All rights reserved.
+
+include $(top_srcdir)/gtester.mk
+
+ext_LTLIBRARIES = libgrlvkontakte.la
+
+libgrlvkontakte_la_CFLAGS =	\
+	$(DEPS_VKONTAKTE_CFLAGS)	\
+	-DG_LOG_DOMAIN=\"GrlVkontakte\" \
+	-DLOCALEDIR=\"$(localedir)\"
+
+libgrlvkontakte_la_LIBADD =	\
+	$(DEPS_VKONTAKTE_LIBS)
+
+libgrlvkontakte_la_LDFLAGS = \
+	-no-undefined	    \
+	-module		    \
+	-avoid-version
+
+libgrlvkontakte_la_SOURCES = grl-vkontakte.c grl-vkontakte.h
+
+extdir				= $(GRL_PLUGINS_DIR)
+vkontaktexmldir	= $(GRL_PLUGINS_DIR)
+vkontaktexml_DATA	= $(VKONTAKTE_PLUGIN_ID).xml
+
+EXTRA_DIST += $(vkontaktexml_DATA)
+
+MAINTAINERCLEANFILES =	\
+	*.in		\
+	*~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/vkontakte/grl-vkontakte.c b/src/vkontakte/grl-vkontakte.c
new file mode 100644
index 0000000..fdc747b
--- /dev/null
+++ b/src/vkontakte/grl-vkontakte.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2015 George Sedov.
+ *
+ * Contact: George Sedov <radist.morse@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <json-glib/json-glib.h>
+#include <glib/gi18n-lib.h>
+#include <grilo.h>
+#include <net/grl-net.h>
+#include <string.h>
+
+#ifdef GOA_ENABLED
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+#define GOA_VK_NAME "vk"
+#endif
+
+#include "grl-vkontakte.h"
+
+#define GRL_VKONTAKTE_SOURCE_GET_PRIVATE(object)          \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                  \
+                               GRL_VKONTAKTE_SOURCE_TYPE, \
+                               GrlVkontakteSourcePrivate))
+
+/* --------- Logging  -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT vkontakte_log_domain
+GRL_LOG_DOMAIN (vkontakte_log_domain);
+
+/* --- URLs --- */
+
+#define VK_API_VER                   "&v=5.28"
+#define URL_VK_BASE                  "https://api.vk.com/method/"
+#define URL_VK_BROWSE                URL_VK_BASE "audio.get?format=json&access_token=%s" VK_API_VER
+#define URL_VK_SEARCH                URL_VK_BASE "audio.search?format=json&q=%s&count=%d&offset=%d&access_token=%s" VK_API_VER
+#define URL_VK_SEARCH_AUTO_COMPLETE  URL_VK_SEARCH "&auto_complete=1"
+
+#define SEARCH_MAX  100
+
+/* --- Plugin information --- */
+
+#define SOURCE_ID       "grl-vkontakte"
+#define SOURCE_NAME     _("VK Music")
+#define SOURCE_DESC     _("A source for browsing and searching VK music")
+
+/* --- GOA names, personalized --- */
+
+#define PERSONAL_SOURCE_ID      "grl-vk-%s"
+#define PERSONAL_SOURCE_NAME    _("%s's VK Music")
+#define PERSONAL_SOURCE_DESC    _("A source for browsing and searching %s's VK music")
+
+struct _GrlVkontakteSourcePrivate {
+  gchar *token;
+  GrlNetWc *grlwc;
+};
+
+struct _GrlSourceReqSpec {
+  GrlSource *source;
+  guint operation_id;
+  GrlSourceResultCb callback;
+  gpointer user_data;
+};
+typedef struct _GrlSourceReqSpec GrlSourceReqSpec;
+
+#ifdef GOA_ENABLED
+struct _GrlVkGoaData {
+  GrlPlugin *plugin;
+  GrlRegistry *registry;
+  GHashTable *sources;
+};
+typedef struct _GrlVkGoaData GrlVkGoaData;
+#endif
+
+/* ================ Forward Declarations ================ */
+
+static GrlVkontakteSource *grl_vkontakte_source_new (GrlPlugin *plugin,
+                                                     const gchar *vk_username,
+                                                     const gchar *vk_token,
+                                                     const gchar *account_id);
+
+static void grl_vkontakte_source_finalize (GObject *object);
+
+static const GList *grl_vkontakte_source_supported_keys (GrlSource *source);
+
+static void grl_vkontakte_source_search (GrlSource *source,
+                                         GrlSourceSearchSpec *ss);
+
+static void grl_vkontakte_source_browse (GrlSource *source,
+                                         GrlSourceBrowseSpec *bs);
+
+#ifdef GOA_ENABLED
+static void grl_vkontakte_goa_init (GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer user_data);
+#endif /*GOA_ENABLED*/
+
+/* ================== Vkontakte Plugin  ================= */
+
+static gboolean
+grl_vkontakte_plugin_init (GrlRegistry *registry,
+                           GrlPlugin *plugin,
+                           GList *configs)
+{
+  GrlConfig *config;
+  gchar *vk_token;
+  gchar *vk_username;
+
+  GRL_LOG_DOMAIN_INIT (vkontakte_log_domain, "vkontakte");
+
+  /* Initialize i18n */
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+  GRL_DEBUG ("Parse provided configs");
+  while (configs) {
+    config = GRL_CONFIG (configs->data);
+    vk_token = grl_config_get_api_token (config);
+    vk_username = grl_config_get_username (config);
+    if (vk_token) {
+      GrlVkontakteSource *source = grl_vkontakte_source_new (plugin, vk_username, vk_token, NULL);
+
+      grl_registry_register_source (registry,
+                                    plugin,
+                                    GRL_SOURCE (source),
+                                    NULL);
+
+      GRL_DEBUG ("Added source from the provided config");
+    } else {
+
+      if (vk_username) {
+        GRL_INFO ("Required API token not provided. "
+                  " Cannot create source for %s", vk_username);
+      } else {
+        GRL_INFO ("Required API token not provided. "
+                  " Cannot create source");
+      }
+    }
+
+    configs = g_list_next (configs);
+  }
+
+#ifdef GOA_ENABLED
+  GrlVkGoaData *user_data = g_slice_new (GrlVkGoaData);
+  user_data->plugin = plugin;
+  user_data->registry = registry;
+
+  goa_client_new (NULL, grl_vkontakte_goa_init, user_data);
+#endif /*GOA_ENABLED*/
+
+  return TRUE;
+}
+
+GRL_PLUGIN_REGISTER (grl_vkontakte_plugin_init, NULL, SOURCE_ID);
+
+/* ================== Vkontakte GObject ================= */
+
+G_DEFINE_TYPE (GrlVkontakteSource, grl_vkontakte_source, GRL_TYPE_SOURCE);
+
+static GrlVkontakteSource *
+grl_vkontakte_source_new (GrlPlugin *plugin,
+                          const gchar *vk_username,
+                          const gchar *vk_token,
+                          const gchar *account_id)
+{
+  GObject *object;
+  GrlVkontakteSource *source;
+  gchar* source_id;
+  gchar* source_name;
+  gchar* source_desc;
+
+  GRL_DEBUG ("vkontakte_source_new");
+
+  if (account_id) {
+    source_id = g_strdup_printf (PERSONAL_SOURCE_ID, account_id);
+  } else {
+    source_id = g_strdup (SOURCE_ID);
+  }
+
+  if (vk_username) {
+    source_name = g_strdup_printf (PERSONAL_SOURCE_NAME, vk_username);
+    source_desc = g_strdup_printf (PERSONAL_SOURCE_DESC, vk_username);
+  } else {
+    source_name = g_strdup (SOURCE_NAME);
+    source_desc = g_strdup (SOURCE_DESC);
+  }
+
+  object = g_object_new (GRL_VKONTAKTE_SOURCE_TYPE,
+                         "source-id", source_id,
+                         "source-name", source_name,
+                         "source-desc", source_desc,
+                         "supported-media", GRL_MEDIA_TYPE_AUDIO,
+                         NULL);
+
+  source = GRL_VKONTAKTE_SOURCE (object);
+
+  source->priv->token = g_strdup (vk_token);
+  source->priv->grlwc = grl_net_wc_new ();
+
+  g_clear_pointer (&source_id, g_free);
+  g_clear_pointer (&source_name, g_free);
+  g_clear_pointer (&source_desc, g_free);
+
+  return source;
+}
+
+static void
+grl_vkontakte_source_class_init (GrlVkontakteSourceClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+  gobject_class->finalize = grl_vkontakte_source_finalize;
+
+  source_class->supported_keys = grl_vkontakte_source_supported_keys;
+  source_class->search = grl_vkontakte_source_search;
+  source_class->browse = grl_vkontakte_source_browse;
+
+  g_type_class_add_private (klass, sizeof (GrlVkontakteSourcePrivate));
+}
+
+static void
+grl_vkontakte_source_init (GrlVkontakteSource *source)
+{
+  GRL_DEBUG ("vkontakte_source_init");
+
+  source->priv = GRL_VKONTAKTE_SOURCE_GET_PRIVATE (source);
+
+  grl_source_set_auto_split_threshold (GRL_SOURCE (source), SEARCH_MAX);
+}
+
+static void
+grl_vkontakte_source_finalize (GObject *object)
+{
+  GRL_DEBUG ("grl_vkontakte_source_finalize");
+
+  GrlVkontakteSource *source = GRL_VKONTAKTE_SOURCE (object);
+
+  g_clear_pointer (&source->priv->token, g_free);
+  g_object_unref (source->priv->grlwc);
+
+  G_OBJECT_CLASS (grl_vkontakte_source_parent_class)->finalize (object);
+}
+
+/* =============== Forward Util Declaration============== */
+
+static void
+vk_req_async_cb (GObject *source_object,
+                 GAsyncResult *res,
+                 gpointer user_data);
+
+static void
+vk_parse_media_cb (JsonArray *array,
+                   guint index_,
+                   JsonNode *element_node,
+                   gpointer user_data);
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_vkontakte_source_supported_keys (GrlSource *source)
+{
+  static GList *keys = NULL;
+  if (!keys) {
+    keys = grl_metadata_key_list_new (/*GRL_METADATA_KEY_ID,*/
+                                      GRL_METADATA_KEY_ARTIST,
+                                      /*GRL_METADATA_KEY_ALBUM,*/
+                                      GRL_METADATA_KEY_DURATION,
+                                      GRL_METADATA_KEY_TITLE,
+                                      /*GRL_METADATA_KEY_TRACK_NUMBER,*/
+                                      GRL_METADATA_KEY_URL,
+                                      GRL_METADATA_KEY_INVALID);
+  }                                   /*Commented for future use*/
+  return keys;
+}
+
+static void
+grl_vkontakte_source_search (GrlSource *source, GrlSourceSearchSpec *ss)
+{
+  gchar *vk_http_search;
+  GrlSourceReqSpec *rs = g_slice_new (GrlSourceReqSpec);
+  rs->source = ss->source;
+  rs->operation_id = ss->operation_id;
+  rs->callback = ss->callback;
+  rs->user_data = ss->user_data;
+
+  vk_http_search = g_strdup_printf (URL_VK_SEARCH,
+                                    ss->text,
+                                    grl_operation_options_get_count (ss->options),
+                                    grl_operation_options_get_skip (ss->options),
+                                    GRL_VKONTAKTE_SOURCE (source)->priv->token);
+
+  grl_net_wc_request_async (GRL_VKONTAKTE_SOURCE (source)->priv->grlwc,
+                            vk_http_search,
+                            NULL,
+                            vk_req_async_cb,
+                            rs);
+}
+
+static void
+grl_vkontakte_source_browse (GrlSource *source, GrlSourceBrowseSpec *bs)
+{
+  gchar *vk_http_browse;
+  GrlSourceReqSpec *rs = g_slice_new (GrlSourceReqSpec);
+  rs->source = bs->source;
+  rs->operation_id = bs->operation_id;
+  rs->callback = bs->callback;
+  rs->user_data = bs->user_data;
+
+  vk_http_browse = g_strdup_printf (URL_VK_BROWSE,
+                                    GRL_VKONTAKTE_SOURCE (source)->priv->token);
+
+  grl_net_wc_request_async (GRL_VKONTAKTE_SOURCE (source)->priv->grlwc,
+                            vk_http_browse,
+                            NULL,
+                            vk_req_async_cb,
+                            rs);
+}
+
+/* ======================= Utilities ==================== */
+
+static inline const char *
+string_or(const char *first, const char *second)
+{
+    return first ? first : second;
+}
+
+#ifdef GOA_ENABLED
+static void
+grl_vkontakte_goa_update (GoaClient *client,
+                          GoaObject *object,
+                          gpointer   user_data)
+{
+  GoaAccount *acc = goa_object_peek_account (object);
+  const gchar *account_id = goa_account_get_id (acc);
+  gboolean status = !(goa_account_get_chat_disabled (acc)); /*TODO: make it music*/
+  GrlVkGoaData *grl_data = (GrlVkGoaData *) user_data;
+
+  if (strcmp (goa_account_get_provider_type (acc), GOA_VK_NAME) != 0) {
+    return;
+  }
+
+
+  GRL_DEBUG("GOA update. id: %s, status: %s", account_id, status?"enabled":"disabled");
+
+  if (status && !g_hash_table_contains (grl_data->sources, account_id)) {
+    /*plugin is enabled, but source is not registered. creating source*/
+    const gchar *vk_username = goa_account_get_presentation_identity (acc);
+    gchar *vk_token = NULL;
+    GError *error = NULL;
+    GrlVkontakteSource *source;
+    gint expires_in;
+
+    if (!goa_oauth2_based_call_get_access_token_sync (goa_object_peek_oauth2_based (object),
+                                                      &vk_token,
+                                                      &expires_in,
+                                                      NULL,
+                                                      &error)) {
+      GRL_ERROR ("Can't get VK token for %s: %s\n", vk_username, error->message);
+      g_error_free (error);
+      return;
+    }
+
+    source = grl_vkontakte_source_new (grl_data->plugin, vk_username, vk_token, account_id);
+
+    if (grl_registry_register_source (grl_data->registry,
+                                      grl_data->plugin,
+                                      GRL_SOURCE (source),
+                                      &error)) {
+      g_hash_table_insert (grl_data->sources, g_strdup (account_id), source);
+      GRL_DEBUG ("GOA update. registered view for id: %s", account_id);
+    } else {
+      GRL_ERROR ("Unable to register VK source for %s: %s\n", account_id, error->message);
+      g_error_free (error);
+      return;
+    }
+  }
+
+  if (!status && g_hash_table_contains (grl_data->sources, account_id)) {
+    /*plugin is disabled, but source is registered. removing source.*/
+    GError *error = NULL;
+    GrlVkontakteSource *source = (GrlVkontakteSource *)g_hash_table_lookup (grl_data->sources, account_id);
+
+    if (grl_registry_unregister_source (grl_data->registry,
+                                        GRL_SOURCE (source),
+                                        &error)) {
+      g_hash_table_remove (grl_data->sources, account_id);
+      GRL_DEBUG ("GOA update. removed view for id: %s", account_id);
+    } else {
+      GRL_ERROR ("Unable to unregister VK source for %s: %s\n", account_id, error->message);
+      g_error_free (error);
+      return;
+    }
+  }
+}
+
+static void
+grl_vkontakte_goa_remove (GoaClient *client,
+                          GoaObject *object,
+                          gpointer   user_data)
+{
+  GoaAccount *acc = goa_object_peek_account (object);
+  const gchar *account_id = goa_account_get_id (acc);
+  GError *error = NULL;
+  GrlVkontakteSource *source;
+  GrlVkGoaData *grl_data = (GrlVkGoaData *) user_data;
+
+  if (strcmp (goa_account_get_provider_type (acc), GOA_VK_NAME) != 0) {
+    return;
+  }
+
+  GRL_DEBUG ("GOA remove. id: %s", account_id);
+  source = (GrlVkontakteSource *)g_hash_table_lookup (grl_data->sources, account_id);
+
+  if (!source) {
+    return;
+  }
+
+  if (grl_registry_unregister_source (grl_data->registry,
+                                      GRL_SOURCE (source),
+                                      &error)) {
+    g_hash_table_remove (grl_data->sources, account_id);
+    GRL_DEBUG ("GOA update. removed view for id: %s", account_id);
+  } else {
+    GRL_ERROR ("Unable to unregister VK source for %s: %s\n", account_id, error->message);
+    g_error_free (error);
+    return;
+  }
+}
+
+static void
+grl_vkontakte_goa_init (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+  GError *error = NULL;
+  GList *tmp;
+  GList *acc_list;
+  GList *vk_acc_list = NULL;
+  GrlVkGoaData *grl_data = (GrlVkGoaData *) user_data;
+
+  GoaClient *cl = goa_client_new_finish (res, &error);
+
+  if (error) {
+    GRL_ERROR ("Can't connect to GOA: %s\n", error->message);
+    g_error_free(error);
+    return;
+  }
+
+  acc_list = goa_client_get_accounts (cl);
+  for (tmp = g_list_first (acc_list); tmp; tmp = g_list_next (tmp))
+  {
+    GoaAccount *acc = goa_object_peek_account (tmp->data);
+
+    if (strcmp (goa_account_get_provider_type (acc), "vk") == 0) {
+      vk_acc_list = g_list_append (vk_acc_list, tmp->data);
+    } else {
+      g_object_unref(acc);
+    }
+  }
+
+  g_list_free (acc_list);
+
+  grl_data->sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  g_signal_connect (cl, "account-added", G_CALLBACK (grl_vkontakte_goa_update), user_data);
+  g_signal_connect (cl, "account-removed", G_CALLBACK (grl_vkontakte_goa_remove), user_data);
+  g_signal_connect (cl, "account-changed", G_CALLBACK (grl_vkontakte_goa_update), user_data);
+
+  for (tmp = g_list_first (vk_acc_list); tmp; tmp = g_list_next (tmp)) {
+    grl_vkontakte_goa_update (cl, (GoaObject *)tmp->data, user_data);
+    g_object_unref (tmp->data);
+  }
+  g_list_free (vk_acc_list);
+}
+#endif /*GOA_ENABLED*/
+
+static void
+vk_req_async_cb (GObject *source_object,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+  gchar *content = NULL;
+  gsize reslen = -1;
+  GError *error = NULL;
+
+  JsonParser *parser;
+  JsonNode   *root;
+  JsonObject *json_obj;
+  JsonArray  *json_array;
+
+  GrlSourceReqSpec *rs = (GrlSourceReqSpec *) user_data;
+
+  if (!grl_net_wc_request_finish (GRL_NET_WC (source_object),
+                                  res,
+                                  &content,
+                                  &reslen,
+                                  &error)) {
+    GRL_ERROR("Network error: %s", error->message);
+    goto out;
+  }
+
+  parser = json_parser_new ();
+
+  if (!json_parser_load_from_data (parser,
+                                   content,
+                                   reslen,
+                                   &error)) {
+    GRL_ERROR("Error parsing json: %s", error->message);
+    goto out;
+  }
+
+  /*{"response":{"count":####,"items":[{"id":####,"owner_id":####,"artist":"####","title":"####","duration":####,"url":"####","lyrics_id":####,"genre_id":####}, ... ]}*/
+
+  /*{"error":{"error_code":####,"error_msg":"####","request_params":[{"key":"####","value":"####"}, ... ]}}*/
+
+  root = json_parser_get_root (parser);
+  if (!JSON_NODE_HOLDS_OBJECT(root)) {
+    error = g_error_new (GRL_CORE_ERROR_SEARCH_FAILED,
+                         0,
+                         "%s",
+                         "VK server sent an incorrect JSON");
+    goto out;
+  }
+
+  json_obj = json_node_get_object (root);
+  if (json_object_has_member (json_obj, "error")) {
+    json_obj = json_object_get_object_member (json_obj, "error");
+    if (!json_obj) {
+      error = g_error_new (GRL_CORE_ERROR_SEARCH_FAILED,
+                           0,
+                           "%s",
+                           "VK server sent an incorrect JSON");
+      goto out;
+    }
+    error = g_error_new (GRL_CORE_ERROR_SEARCH_FAILED,
+                         json_object_get_int_member (json_obj, "error_code"),
+                         "%s",
+                         string_or (json_object_get_string_member (json_obj, "error_msg"), "VK server sent an incorrect JSON"));
+    goto out;
+  }
+
+  json_obj = json_object_get_object_member (json_obj, "response");
+  if (!json_obj) {
+    error = g_error_new (GRL_CORE_ERROR_SEARCH_FAILED,
+                         0,
+                         "%s",
+                         "VK server sent an incorrect JSON");
+    goto out;
+  }
+
+  if (json_object_get_int_member (json_obj, "count") == 0) {
+    /*No results*/
+    goto out;
+  }
+
+  json_array = json_object_get_array_member (json_obj, "items");
+  if (!json_array) {
+    error = g_error_new (GRL_CORE_ERROR_SEARCH_FAILED,
+                         0,
+                         "%s",
+                         "VK server sent an incorrect JSON");
+    goto out;
+  }
+
+  json_array_foreach_element (json_array,
+                              vk_parse_media_cb,
+                              user_data);
+
+out:
+  /*finish the search operation*/
+  rs->callback (rs->source, rs->operation_id,
+                NULL, 0, rs->user_data, error);
+
+  g_slice_free (GrlSourceReqSpec, rs);
+  g_object_unref (parser);
+}
+
+static void
+vk_parse_media_cb (JsonArray *array,
+                   guint index_,
+                   JsonNode *element_node,
+                   gpointer user_data)
+{
+  JsonObject *json_obj;
+  GrlSourceReqSpec *rs = (GrlSourceReqSpec *) user_data;
+  GrlMedia *media;
+  const gchar *url;
+
+  if (!JSON_NODE_HOLDS_OBJECT(element_node)) {
+    GRL_WARNING ("erroneous media JSON in VK response");
+    return;
+  }
+
+  /*{"id":####,"owner_id":####,"artist":"####","title":"####","duration":####,"url":"####","lyrics_id":####,"genre_id":####}*/
+  json_obj = json_node_get_object (element_node);
+  url = json_object_get_string_member (json_obj, "url");
+  if (!url) {
+    GRL_WARNING ("media in VK response doesn't contain url");
+    return;
+  }
+  media = grl_media_audio_new ();
+  grl_media_audio_set_artist (GRL_MEDIA_AUDIO(media), string_or (json_object_get_string_member (json_obj, "artist"), _("Unknown")));
+  grl_media_set_title (media, string_or (json_object_get_string_member (json_obj, "title"), _("Unknown")));
+  grl_media_set_duration (media, json_object_get_int_member (json_obj, "duration"));
+  grl_media_set_url (media, url);
+
+  rs->callback (rs->source,
+                rs->operation_id,
+                media,
+                GRL_SOURCE_REMAINING_UNKNOWN,
+                rs->user_data,
+                NULL);
+}
diff --git a/src/vkontakte/grl-vkontakte.h b/src/vkontakte/grl-vkontakte.h
new file mode 100644
index 0000000..955f2d4
--- /dev/null
+++ b/src/vkontakte/grl-vkontakte.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 George Sedov.
+ *
+ * Contact: George Sedov <radist.morse@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_VKONTAKTE_H_
+#define _GRL_VKONTAKTE_H_
+
+#include <grilo.h>
+
+#define GRL_VKONTAKTE_SOURCE_TYPE                        \
+  (grl_vkontakte_source_get_type())
+
+#define GRL_VKONTAKTE_SOURCE(obj)                        \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),                     \
+                              GRL_VKONTAKTE_SOURCE_TYPE, \
+                              GrlVkontakteSource))
+
+#define GRL_VKONTAKTE_SOURCE_CLASS(klass)                \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                      \
+                           GRL_VKONTAKTE_SOURCE_TYPE,    \
+                           GrlVkontakteSourceClass))
+
+#define GRL_IS_VKONTAKTE_SOURCE(obj)                     \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                    \
+                           GRL_VKONTAKTE_SOURCE_TYPE))
+
+#define GRL_IS_VKONTAKTE_SOURCE_CLASS(klass)             \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),                      \
+                           GRL_VKONTAKTE_SOURCE_TYPE))
+
+#define GRL_VKONTAKTE_SOURCE_GET_CLASS(obj)              \
+  (G_TYPE_INSTANCE_GET_CLASS((obj),                      \
+                              GRL_VKONTAKTE_SOURCE_TYPE, \
+                              GrlVkontakteSourceClass))
+
+/* plugin's log domain */
+GRL_LOG_DOMAIN_EXTERN(vkontakte_log_domain);
+
+typedef struct _GrlVkontakteSource        GrlVkontakteSource;
+typedef struct _GrlVkontakteSourcePrivate GrlVkontakteSourcePrivate;
+
+struct _GrlVkontakteSource {
+  GrlSource parent;
+  GrlVkontakteSourcePrivate *priv;
+};
+
+typedef struct _GrlVkontakteSourceClass GrlVkontakteSourceClass;
+
+struct _GrlVkontakteSourceClass {
+  GrlSourceClass parent_class;
+};
+
+GType grl_vkontakte_source_get_type(void);
+
+#endif /* _GRL_VKONTAKTE_H_ */
diff --git a/src/vkontakte/grl-vkontakte.xml b/src/vkontakte/grl-vkontakte.xml
new file mode 100644
index 0000000..1d075b3
--- /dev/null
+++ b/src/vkontakte/grl-vkontakte.xml
@@ -0,0 +1,10 @@
+<plugin>
+  <info>
+    <name>Vkontakte</name>
+    <module>libgrlvkontakte</module>
+    <description>A plugin for searching music</description>
+    <author>George Sedov</author>
+    <license>LGPL</license>
+    <site>https://wiki.gnome.org/Projects/Grilo</site>
+  </info>
+</plugin>